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

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

ומה איתנו?

23/03/2019

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

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

ומה איתנו?

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

והתשובה כמובן שיש משהו שנשאר. שמתכנתת עם 5 שנים ניסיון בפיתוח Java שמגיעה ללמוד Objective C תסתכל על הטכנולוגיה בצורה אחרת לגמרי מתכנתת שרק עכשיו סיימה לימודים. ושלמרות שאולי לשתיהן כתוב בקורות חיים ״שנה ניסיון בפיתוח אפליקציות ל iPhone״, ה-5 שנים הקודמות גורמות לזה ששנה של אחת שווה יותר מהשנה של השניה.

וקל לראות אני חושב שהדרך הכי טובה לשפר את הגישה שלנו לטכנולוגיה היא בדיוק להתחיל מחדש. וכמה שיותר יותר טוב.

פשוט תעוף על זה

22/03/2019

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

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

הרבה יותר מועיל להתאים את רמת המשחק לרמת השחקן. השלבים נראים בערך כך:

  1. קח את הקוד הזה ותקליד אותו אצלך ותראה שעובד.

  2. קח את הקוד הזה שהקלדת ותשנה את הצבעים.

  3. קח את האפליקציה הזאת ותוסיף לה פיצ'ר.

  4. קח את הרעיון הזה ותבנה ממנו אפליקציה.

  5. פשוט תעוף על זה :)

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

מימוש יישום מרובה שפות באמצעות React Context

21/03/2019

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

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

  1. מדובר על מידע גלובאלי שכל רכיב ביישום משתמש בו.

  2. מדובר על מידע שביסודו הוא Immutable, או לפחות שלא משתנה לעתים קרובות.

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

דוגמאות נפוצות ל Context כוללות: הגדרות שפה, הגדרות עיצוב גלובאליות ו Redux Store.

המשך קריאה

הפונקציה 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
git

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

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

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

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

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

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

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

המשך קריאה

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

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

בדיוק כמו השיחה הדימיונית שלמעלה, מנגנון 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 עוטף שדווקא כן מסתדר.