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

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

הפונקציה useEffect פותרת את אחת הבעיות הישנות ביותר שאני זוכר עם ריאקט

20/03/2019

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

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

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

כשמשלבים את שתי הדרישות האלה נופלים לבור: בשביל לשלוף את המידע מהשרת כשהקומפוננטה מצטרפת לעמוד נצטרך לכתוב את קוד התקשורת ב componentDidMount. אבל בשביל לטפל בכל עדכון של ה id נצטרך לכתוב את קוד התקשורת ב componentDidUpdate. התוצאה העצובה היתה תמיד קומפוננטה שמימשה את שתי הפונקציות, ומתוך כל אחת משתיהן קראה לפונקציה שלישית שמטפלת בתקשורת.

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

ומסתבר שכל הבלאגן הזה נפתר היום עם מילת קסם אחת שנקראת useEffect:

function Character(props) {
  const [data, setData] = useState({});

  useEffect(function() {
    const req = $.get(`https://swapi.co/api/people/${props.id}`, function(data) {
      setData(data);
    });
    return function() {
      req.abort();
    }
  }, [props.id]);

  return <p>id = {props.id}, name = {data.name}</p>
}

וכאן יש קודפן כדי להוכיח שזה עובד (גם אני לא האמנתי):

נ.ב. אל תתרגלו לזה. החברים בריאקט עובדים על פיתרון עוד יותר פשוט שנקרא Suspense. הוא יהיה מוכן איפשהו בחודשים הקרובים והוא ייתן לנו מנגנון דומה ל async/await בתוך פונקציית render של פקד מסוג פונקציה. הולך להיות מעניין.

למה דווקא Git?

19/03/2019

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

  1. שהמערכת תהיה מבוזרת.

  2. שהמערכת תהיה אמינה.

  3. שהמערכת תעבוד מהר.

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

I can build something better in two weeks -- Linus Torvalds

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

המשך קריאה

מהי Callback Function ולמה שבכלל יהיה לכם אכפת ממנה?

18/03/2019

״רוצה לכתוב לי את זה כאן?״ ״לא עזוב זה ארוך... אתקשר אליך בערב״

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

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

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

בואו נפרק את זה כדי לראות את המבנה:

function myCallbackFunction() {
  $('p').text('Thanks!');
}

$('button').on('click', myCallbackFunction);

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

  1. הפונקציה on מקבלת שני פרמטרים: הראשון הוא שם האירוע והשני הוא ה Callback Function. אפשר לחשוב על זה כאילו בהפעלת הפונקציה on ביקשנו מידע - וזה באמת יהיה נכון. ביקשנו לדעת מי לחץ על הכפתור. הבעיה היחידה היא שאף אחד עדיין לא לחץ על הכפתור. לכן אני מעביר את פונקציית ה״צלצל אליי בערב״, או בשמה המקצועי את ה Callback Function. כשמישהו ילחץ על הכפתור הפונקציה on תתעורר ותפעיל את פונקציית ה Callback שלי.

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

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

והנה הקוד המפורק מתוכו:

function myCallback(data) {
  $('pre').html(JSON.stringify(data));
}

$.get('https://swapi.co/api/people/1', myCallback);

ושוב אותו מבנה - הפונקציה $.get היא שאחראית על הבאת המידע, אבל אם נסתכל על ערך ההחזר שלה נראה שם תוצאה ריקה. המידע עדיין לא הגיע. הפרמטר השני לפונקציה הוא ה Callback Function, והפונקציה באופן אוטומטי תפעיל אותו כשהתוצאה שלה תהיה מוכנה.

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

function getHairColor(id) {
  function myCallback(data) {
    $('pre').html(data.hair_color);
  }

  $.get(`https://swapi.co/api/people/${id}`, myCallback);
}

זה עובד כי הפונקציה כותבת את צבע השיער ל HTML. אבל מה אם נרצה לקחת את זה צעד קדימה? מה אם נרצה שהפונקציה תחזיר את צבע השיער? אולי לכתוב קוד שנראה ככה:

const hairColor = getHairColor(1);
alert("Luke's hair color is ", hairColor);

אני מקווה שעכשיו אתם כבר רואים שזה לא הולך לעבוד. הפונקציה getHairColor מחזירה את הערך לפני שהיא סיימה לקבל את המידע ב Ajax. בשביל להציג את צבע השיער מבחוץ אנחנו צריכים לשנות את המבנה שלה כך שהיא תקבל Callback Function. בדיוק כמו on. בדיוק כמו get.

הנה דרך אחת בה זה יכול לעבוד:

function getHairColor(id, fn) {
  function myCallback(data) {
    fn(data.hair_color);
  }

  $.get(`https://swapi.co/api/people/${id}`, myCallback);
}

getHairColor(1, function(hairColor) {
  alert(hairColor);
});

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

איך (ולמה) לכתוב React Hook

17/03/2019

ריאקט עברה ועוברת עדיין הרבה טלטלות מבחינת ה API, כאשר אולי הגדולה ביותר היתה ההמלצה להפסיק להשתמש ב React.createClass ולעבור להשתמש במחלקות וב Function Components כדי ליצור את הקומפוננטות.

נודה על האמת הפונקציה React.createClass היתה סוג של פשרה מהיום הראשון. הרי אם היו class-ים ב JavaScript כשריאקט התחילה כנראה לא היו צריכים לכתוב אותה. לפונקציה זו מקבילה בהרבה ספריות UI אחרות ישנות יותר כמו למשל Class.create של פרוטוטייפ או declare של dojo.

אבל משהו מאוד גדול הלך לאיבוד במעבר מ React.createClass ל class המודרני, ולמשהו הזה קוראים Mixins. מיקסינס הפכו במעבר למחלקות למבנה שנקרא Higer Order Components שהיה הרבה פחות אינטואיטיבי לרוב המפתחים. בפוסט היום ניזכר יחד מה היו מיקסינס, איך HoC אמורים היו להחליף אותם ומה Hooks מצליחים לעשות טוב יותר. מוכנים?

המשך קריאה

לא מה שחשבת

16/03/2019

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

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

או בדוגמא אחרת, בעיית אבטחה שהתגלתה בקורקינטים של שיאומי מנצלת את העובדה שהקורקינט מתקשר באמצעות Bluetooth בלי לבצע תהליך Pairing או אותנטיקציה של המכשיר שנותן לו פקודות. אי אפשר לבוא בטענות למי שהמציא את המנגנון כי יש מצבים בהם זה בדיוק מה שאנחנו רוצים (דמיינו רדיו שכל אחד יכול להזרים אליו שירים במסיבה), אבל כששמים מנגנון כזה על קורקינט ציבורי מתחילות הבעיות.

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

תתחילו עם המחיר

15/03/2019

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

אבל האמת שעדיף ללכת הפוך: אני רוצה להרוויח X ש"ח בחודש; מה מעצבים גרפיים שמרוויחים את הסכום הזה עושים? איך הם הגיעו לאן שהם הגיעו? נשמע שכדאי ללכת באותו כיוון.

יכול להיות שבסוף תעצבו דפי נחיתה, או שתהיו מורים לעיצוב גרפי, או שבכלל תגלו שהמעצבים הגרפיים שמרוויחים הכי טוב הם אלה שיודעים לכתוב גם HTML ו-CSS ותלכו ללמוד את זה. הדבר היחיד שחשוב כאן הוא נקודת המבט- אתם בוחרים מה אתם רוצים להיות, אתם בוחרים מה אתם רוצים לעשות, ואתם בוחרים כמה אתם רוצים להרוויח. אי אפשר לנצח בכל 3 הצלעות של המשולש הזה, זה ברור. אבל כן אפשר לבחור את המקום שלכם.

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

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

כשה API לא מסתדר עם האינטואיציה

14/03/2019

אחת השאלות הראשונות של אנשים שעבדו עם React Hooks היתה קשורה לשילוב, או יותר נכון להתנגשות, בין React Hooks ל setInterval. בקצרה מסתבר שהקוד הבא לא עובד:

function Counter() {
  let [count, setCount] = useState(0);

  useEffect(() => {
    let id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <h1>{count}</h1>;
}

ה Counter בפקד נעצר ב-1.

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

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

import React, { useState, useEffect, useRef } from 'react';

function Counter() {
  let [count, setCount] = useState(0);

  useInterval(() => {
    // Your custom logic here
    setCount(count + 1);
  }, 1000);

  return <h1>{count}</h1>;
}

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

כל מה שרציתם לדעת על Git Rebase ולא ידעתם את מי לשאול

13/03/2019

בעבודה עם גיט ברוב המקרים נרצה שהיסטוריית הפרויקט תשקף בצורה מהימנה את מה שבאמת קרה שם. אבל מה אם מה שקרה שם לא היה כזה יפה? במצבים כאלה ריבייס מאפשר לנו לשנות את היסטוריית הפרויקט ולכתוב אותה כמו שהיינו רוצים לקרוא אותה. בואו נראה איך זה עובד ומספר Use Cases לשימוש במנגנון זה.

המשך קריאה

במורד מחילת הארנב

12/03/2019

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

וזה יכול להימשך שעות. אפילו ימים.

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

ההבדל בין להבין ללדעת

11/03/2019

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

https://www.youtube.com/watch?v=qs0b2t8SM88&t=647s

סיכוי טוב מאוד שאחרי שתצפו בסרטון תרגישו שהבנתם. אפילו אם תצפו במהירות 1.5.

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

זה נחמד להבין איך בוט לטלגרם עובד, אבל אם אני צריך מישהו שיעזור לי לכתוב בוט לטלגרם אני אתאמץ למצוא מישהו שיודע.