שלום אורח התחבר

הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

מעדיפים לקרוא מהטלגרם? בקרו אותנו ב:@tocodeil

או הזינו את כתובת המייל וקבלו את הפוסט היומי בכל בוקר אליכם לתיבה:

בעבודה עם TypeScript כפי שקל להבין לפי שם השפה, אנחנו צריכים להגדיר טיפוס לכל משתנה. עם רוב המשתנים זה לא בעיה אבל מדי פעם אתם רוצים להגיד משהו פרוע כמו "אני מוכן לקבל כל מה ש input מוכן לקבל". לדוגמה בקומפוננטה הזו שמייצרת input יחד עם label:

function InputWithLabel(props: {...}) {
    const { label } = props;
    return (
        <label>{label}</label>
        <input type="text" {...props} />
    );
}

ל TypeScript יש פיתרון נוח למצבים אלה שנקרא React.ComponentProps וזה נראה כך:

type InputProps = React.ComponentProps<'input'>

עכשיו אפשר לחזור לקומפוננטה שלנו ולכתוב:

function InputWithLabel(props: { label: string } & InputProps) {
  const { label } = props;
  return (
    <>
      <label>{label}</label>
      <input type="text" {...props} />
    </>
  );
}

אחד הפוסטים שעלה היום בהאקרניוז סיפר על סימן חלוקה חמוד ב 19:

  1. מסתכלים על מספר מימין לשמאל (סיפרה אחרי סיפרה)

  2. כופלים את הסיפרה הימנית ביותר ב 2 ומחברים לה את הסיפרה הבאה

  3. את התוצאה כופלים ב 2 ומחברים לה את הסיפרה הבאה

  4. וכך ממשיכים עד שהגענו למספר קטן יותר, שאולי לגביו נדע אם הוא מתחלק ב-19.

בקוד זה נראה כך:

function reduceMod19(n) {
  let accumulator = 0;
  while (n > 0) {
    let digit = n % 10;
    accumulator = accumulator * 2 + digit;
    n = Math.floor(n / 10);
  }

  return accumulator;
}

הקוד עושה בדיוק מה שהוא צריך לעשות אבל מבחינת קריאות אני חושב שאפשר לשפר: כשאני מסתכל על הלולאה במבט ראשון לא ברור שזו לולאה שרצה על כל הספרות של המספר מימין לשמאל. זה משהו שאני צריך לקרוא ממש את הקוד בשביל להבין אותו.

המטרה של פקודת reduce היא להוציא החוצה את הסיפור הזה. לבוא ולהגיד לקורא - שמע יש פה משהו שרץ על כל האלמנטים של הרשימה אחד אחד. יותר מזה, הדבר הזה יחזיר לך תוצאה בודדת שתהיה מושפעת קצת מכל אלמנט. במילים אחרות reduce לוקחת את כל הקוד והופכת אותו למשפט:

digits.reduce((acc, val) => acc * 2 + val, 0);

אני הולך לעבור על כל הרשימה שנקראת digits אחד-אחד, ועבור כל אלמנט אני אכפול את מה שחישבתי עד עכשיו ב-2 ואוסיף את הדבר החדש.

כמובן בשביל שזה יעבוד אני צריך להפוך את המספר לרשימה של ספרות מהסוף להתחלה וסך הכל שיכתוב הפונקציה נראה כך:

function reduceMod19(n) {
  let digits = String(n).split('').map(d => Number(d));
  return digits.reverse().reduce((acc, val) => acc * 2 + val, 0);
}

התוצאה היא קוד שמספר טוב יותר את הסיפור שלו: במקום להציג לולאה כללית בה אני צריך להתאמץ כדי להבין מה המבנה, אני מקבל כאן מבנה מאוד ספציפי וצריך להתאמץ רק בשביל להבין מה עושים עם כל אלמנט.

כמובן שמשיקולי ביצועים קשה להאמין שהייתי כותב קוד כזה בעולם האמיתי בהקשר הזה של סימן חלוקה ב 19, ובכל זאת שווה להתרגל לשתי הגישות ולהתיחס ל reduce בתור עוד סוג של לולאה - לולאה שמספרת סיפור יותר משמעותי מעוד לולאת for.

מתכנתים לא אוהבים לכתוב CSS ויש לזה כל מיני סיבות. אפילו אנשי Front End רבים שפגשתי העדיפו להתמקד בצד הטכני יותר של פיתוח JavaScript או React ולהשאיר את ה CSS למעצבים. במקביל ככל שהעולם של ה CSS התפתח כך זה רק עוד יותר הפחיד רבים מאיתנו: עכשיו ללמוד Flexbox ו grid? ובאיזה דפדפנים כל דבר עובד? וממילא מה שאני לא אעשה זה לא יראה טוב אז בשביל מה להתאמץ.

צריך להגיד לכתוב CSS כשלא יודעים CSS זו באמת חוויה קשה, וגם כשיודעים CSS יש בכתיבה שלו אתגרים שונים מאלה של כתיבת קוד. הנה כמה טיפים שעוזרים לי להיות פחות מתוסכל כשאני כותב CSS ואני מקווה שיעזרו גם לכם:

המשך קריאה...

אחד הדברים שהכי קשה להכיל במעבר ל react-testing-library הוא החשיבות הגדולה שמפתחי הספריה מעניקים ל Accessibility Roles. בעוד רוב ספריות הבדיקה נותנות לנו להגיע לאלמנטים באיזו צורה שאנחנו רוצים (דרך CSS או אפילו XPath), כשמגיעים ל react-testing-library מגלים ש getByCSS זו לא פונקציה אמיתית שם.

השינוי הטכני הזה נובע משינוי תפיסה לגבי תפקיד הבדיקה ומה אנחנו רוצים לבדוק: בעוד ספריות בדיקה מסורתיות רוצות לבדוק שדברים מגיעים ל DOM בצורה נכונה, ב react-testing-library אנחנו רוצים לבדוק יותר מזה, אנחנו רוצים לבדוק שאנחנו מבינים את העמוד שלנו כמו שמשתמש מבין אותו. המעבר ל getByRole מהווה שינוי בנקודת המבט.

שימו לב לקטע הבא לדוגמה שהצגתי בוובינר אתמול:

export default function SimpleList({ items }) {
  const [filter, setFilter] = useState('');

  return (
    <>
      <input
        type="search"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        name="filter-list"
      />
      <ul>
        {items.filter(item => item.text.includes(filter)).map((item, idx) => (
          <li
            key={item.id}
            className={(idx + 1) % 2 === 0 ? "even" : "odd"}
          >
            {item.text}
          </li>
        ))}
      </ul>
    </>
  );
}

בספריית בדיקות רגילה הייתי יכול להשתמש ב CSS כדי להגיע לאלמנט ה input ולמשל לקרוא ל:

document.querySelector('input[name="filter-list"]')

אבל במעבר ל react-testing-library ובפרט אם אני מוכן לקבל את השגעונות שלהם ולהשתמש ב getByRole אני אגלה שקשה לי לתפוס את אלמנט ה input הזה. הקוד הבא יעבוד:

const el = screen.getByRole('searchbox');

נו וזה עובד כל עוד יש תיבה אחת על המסך. הוספה של תיבה נוספת לקומפוננטה תשבור את הבדיקה, ואין לי דרך באמצעות getByRole לציין אף אחד מהמאפיינים של ה input כדי שיעזרו לי לבחור את האלמנט שאני רוצה.

המעבר ל getByRole מכריח אותי לחשוב כמו המשתמש - איך המשתמש יודע על איזו תיבה מדובר? איך המשתמש יודע איפה להכניס את הקלט? משתמש לא רואה את המאפיין name או את הקלאסים של התיבה. הוא יכול לראות את הטקסט שרשום בה אבל בדרך כלל הוא יראה את ה Label שמשויך לתיבת הקלט. וכבר אנחנו רואים את הבעיה בקומפוננטה: ל input אין label שמסביר מה היא עושה.

תיקון של הקוד והוספת label נראה כך:

export default function SimpleList({ items }) {
  const [filter, setFilter] = useState('');

  return (
    <>
      <label>Filter List

        <input
          type="search"
          value={filter}
          onChange={(e) => setFilter(e.target.value)}
        />
      </label>

      <ul>
        {items.filter(item => item.text.includes(filter)).map((item, idx) => (
          <li
            key={item.id}
            className={(idx + 1) % 2 === 0 ? "even" : "odd"}
          >
            {item.text}
          </li>
        ))}
      </ul>
    </>
  );
}

ושימו לב לקסם - עכשיו אפשר להשתמש בטקסט ב label כדי לזהות את ה input שהסתכלנו עליו:

screen.getByRole('searchbox', { name: 'Filter List' } );

הנה שאלה פשוטה בשבילכם שבכיף אפשר לשאול בראיונות עבודה או במסיבות (למרות שתכל'ס אין יותר מסיבות עכשיו עם הקורונה, וגם ראיונות עבודה הם הרבה פחות כיפיים).

אנחנו יודעים שהפונקציה Array.prototype.fill ממלאת מערך בערך שנעביר לה, ולכן הקוד הבא מחזיר 40:

new Array(4).fill(10).reduce((acc, val) => acc + val);

ואנחנו גם יודעים שאפשר לשנות חלק מהערכים אחרי שהפעלנו fill כך שלדוגמה הקוד הבא יחזיר 50:

const data = new Array(4).fill(10);
data[0] += 10;

data.reduce((acc, val) => acc + val);

אבל מה יקרה אם ננסה לעשות את אותו טריק עם אוביקטים... מה יחזיר הקוד הבא? ולמה?

data.reduce((acc, val) => acc + val.value, 0)

כבר שנים שהבחירה הפופולרית של מפתחי APIs שצריכים להחליט על מבנה הממשק נקראת REST. המעבר ל REST התרחש בצורה הדרגתית והחליף ממשקים מבוססי XML שקדמו לו והוא בא יחד עם המעבר להעברת מידע באמצעות JSON. אנחנו אוהבים REST כי מאוד פשוט להבין ממשקי תכנות שבנויים כך, אבל ל REST יש בעיה רצינית ככל שהממשקים גדלים וצריך להפריד בין קוד השרת לקוד הלקוח.

בפוסט זה נדבר על הבעיות של REST, על GraphQL ונראה דוגמאות קוד ראשונות לעבודה עם GraphQL.

המשך קריאה...

סרביס וורקר הוא אחד הממשקים המורכבים של פיתוח Web אבל גם כלי שמאפשר התנהגויות מורכבות שלא ראינו לפני ויכול לעזור לנו להציע חווית שימוש טובה יותר למשתמשים שלנו.

בפוסט זה אספר לכם על Service Worker בצורה פשוטה ודרך מספר דוגמאות כדי שיהיה לכם קל להבין אם אתם צריכים Service Worker ביישום שלכם, ואם כן גם יהיו לכם את הכלים לכתוב אותו.

המשך קריאה...

אחת הלולאות שהיו פעם פופולריות ב JavaScript אבל בשנים האחרונות מתכנתים כמעט והפסיקו להשתמש בהן היא לולאת for...in. לולאה זו נועדה לרוץ על כל המפתחות של אוביקט למשל כדי להדפיס את כל תוכן האוביקט או לחפש מפתח של ערך מסוים.

בדוגמא מעשית נניח שיש לנו אוביקט שמחזיק כתובות מייל של משתמשים:

const email = {
    bob: 'bob@gmail.com',
    jane: 'jane@gmail.com',
    dave: 'dave@hotmail.com',
};

אז נוכל להשתמש בלולאת for...in כדי להדפיס את כל האנשים שיש להם כתובת מייל בג'ימייל:

for (let name in email) {
    const address = email[name];
    if (address.endsWith('@gmail.com')) {
        console.log(name);
    }
}

אבל - לולאת for...in כוללת גם מוקש קטן. המפתחות שחוזרים הם לא רק המפתחות מהאוביקט שאתם סורקים, אלא גם המפתחות מכל ה Prototypes של אותו אוביקט. לכן הקוד הבא ידפיס 3:

let count = 0;
const one = { a: 10, b: 20 };
const two = Object.create(one);
two.c = 30;

for (let k in two) {
    // count the keys
    count ++;
}

console.log(count);

הלולאה ספרה שני מפתחות מהאוביקט one ועוד מפתח אחד מהאוביקט two וכך הגיעה ל-3. כשיש לנו אוביקט ביד לא תמיד אנחנו יודעים לצפות מי ה Prototype שלו ואיזה מאפיינים הגיעו מאותו Prototype, ולכן לולאות for...in עלולות לרוץ על יותר מפתחות ממה שציפינו.

בקוד JavaScript חדש אנחנו מעדיפים להשתמש בפונקציה Object.keys שמחזירה רק את המפתחות של האוביקט שהעברתם לה. קוד מקביל עם Object.keys יראה כך והפעם ידפיס 1:

let count = 0;
const one = { a: 10, b: 20 };
const two = Object.create(one);
two.c = 30;

for (let k of Object.keys(two)) {
    // count the keys
    count ++;
}

console.log(count);

לא משנה כמה אנחנו אוהבים את האונליין, יש כיף בללמוד יחד עם עוד אנשים: כשיש לך את מי לשתף בפיתרון המדליק שכתבת, כשיש את מי לשאול כשנתקעים, כשיש פידבק ממרצה וגם מחברים על העבודה שלך - פידבק שעוזר להשתפר.

מי שעוקב פה יודע שאני הרבה זמן מחפש את הנוסחא שתאפשר ללמוד יחד בכיף גם מחוץ לכיתה, ועכשיו עם הקורונה מסביב החיפוש הזה הפך הרבה יותר חשוב. בשביל זה פתחתי היום רישום לשני מסלולי לימוד חדשים שאני חושב שיכולים לעניין גם אתכם.

המסלולים מיועדים לאנשים שרוצים ללמוד עם עוד חברים, רוצים תוכנית לימודים מסודרת, שמחים להשקיע שעות בכתיבת קוד ובשיחה עליו אבל בינתיים לא מצאו את המסגרת המתאימה. אנשים שיודעים שההשקעה היא חשובה ושהתמדה היא המפתח להצלחה, אבל כל פעם שמתיישבים ללמוד או לתרגל נתקעים מהר ומתקשים למצוא את הכוחות להמשיך לריב עם התרגיל עד שיעבוד.

המסלול הראשון מיועד ללימוד React עבור מתכנתי ווב שיודעים כבר JavaScript. הוא יימשך שלושה שבועות במהלכם אנחנו נעבוד יחד כל יום לפי תוכנית עבודה מסודרת. פעמיים בשבוע נקיים מפגשי אונליין בהם תציגו את העבודות שלכם ותוכלו להעלות שאלות, ובשאר השבוע נתקשר דרך קבוצת צ'ט. המסלול מבוסס על תכני הוידאו והתרגילים של קורס React כאן באתר, והוא מתאים גם אם ראיתם כבר את הסרטים אבל לא פתרתם עדיין את כל התרגילים והפרויקטים.

המסלול השני מיועד ללימוד Python והוא יעבוד במתכונת דומה. גם הוא יימשך שלושה שבועות ויכלול עבודה יומית לפי תוכנית מסודרת, לימוד דרך סרטי הוידאו המוקלטים באתר, קבוצת צ'ט לתרגול ושאלות כל השבוע ומפגשי אונליין לשיתוף ידע והצגת העבודות שלכם כדי שתוכלו לקבל פידבק מחברים וממני.

שני המסלולים לא מיועדים למי שרוצה "ללמוד ריאקט תוך כדי שינה", אלא למי שרוצה לשבת ללמוד דרך התמודדות עם בעיות ובליווי מנחה וקבוצת חברים. אני חושב ששניהם יהיו מהנים מאוד. עכשיו לפרטים הטכניים:

מסלול בוטקמפ ללימוד ריאקט ייפתח בתאריך 14.06.2020 למשך שלושה שבועות. מפגשי העבודה יתקיימו בימי ראשון ורביעי בין השעות 9:00 ל-11:00 בבוקר בתאריכים: 14/06, 17/06, 21/06, 24/06, 28/06, 01/07. מחיר הקורס 970 ש"ח וזה כולל חודש מנוי לתקופה של הקורס (אם אתם כבר מנויים החודש הזה יתווסף לכם אוטומטית לתקופת המנוי). הצטרפות לבוטקמפ בקישור: https://www.tocode.co.il/quickjoin2?term=15.

מסלול בוטקמפ ללימוד פייתון יפתח בתאריך 14.06.2020 למשך שלושה שבועות. מפגשי העבודה יתקיימו בימי ראשון ורביעי בין השעות 13:00 ל-15:00 בצהריים בתאריכים: 14/06, 17/06, 21/06, 24/06, 28/06, 01/07. מחיר הקורס 970 ש"ח וזה כולל חודש מנוי לתקופה של הקורס (אם אתם כבר מנויים החודש מתווסף אוטומטית לתקופת המנוי). הצטרפות לבוטקמפ פייתון בקישור: https://www.tocode.co.il/quickjoin2?term=16.

מוזמנים לשלוח מייל או להשאיר הודעה באתר לכל שאלה.

הפוסט הזה הוא פרק לדוגמא מקורס Front End שאני כותב עכשיו מחדש. מקווה שתהנו ממנו או אם אתם כבר מכירים את הנושא ספרו לי מה דעתכם על ההסבר והדוגמא.

המשך קריאה...