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

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

מה דוקר יכול לעשות בשבילכם?

26/02/2019

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

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

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

המשך קריאה

גזלן

25/02/2019

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

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

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

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

שלושה סוגים של Delegation

24/02/2019

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

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

המשך קריאה

אינטואיציה

23/02/2019

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

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

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

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

חדש בבלוג: ערוץ טלגרם

22/02/2019

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

כתובת הערוץ החדש ישן היא:

@tocodeil

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

  def publish_daily_post_content do
    href = App.Tocode.daily_post_url <> "/md"
    headers = []
    options = [hackney: [pool: :default]]

    {:ok, response} = HTTPoison.get(href, headers, options)
    Nadia.send_message("@tocodeil", response.body, parse_mode: "Markdown")
  end

לפני שפותחים את השמפניה כמה דברים שעדיין לא עשיתי:

  1. הבוט לא שומר שבת ולכן אם מפריע לכם לקבל הודעות בשבתות וחגים בבוקר או שלא אוהבים את השעה או כל דבר כזה אני מצטער תצטרכו לכתוב בוט לעצמכם (או לשלוח Pull Request כי הבוט שלי בקוד פתוח).

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

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

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

https://github.com/ynonp/tocode_bot

אז איך עובד HTTPS?

21/02/2019

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

המשך קריאה

חלום לא ריאליסטי

20/02/2019

שני סוגים של חלומות לא ריאליסטים:

  1. אף אחד אף פעם לא הצליח לעשות את זה.

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

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

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

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

תפריט לפיתוח אפליקציות ווב

19/02/2019

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

המשך קריאה

איך למצוא ולתקן render מיותר ביישום ריאקט

17/02/2019

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

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

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

מימוש ברירת המחדל של shouldComponentUpdate מחזיר תמיד true, מה שאומר שכל שינוי ב props או ב state של הפקד יביא להפעלת render מחדש. מחלקת אב בשם React.PureComponent מספקת מימוש קצת יותר יעיל שמבצע השוואת Shallow Comparison בין ה props החדש לישן ובין ה state החדש לישן, ורק אם יש הבדל יחזיר true.

לכן בשביל לזהות ולתקן פעולות render מיותרות נרצה לבצע:

  1. לוודא ש render מופעל יותר פעמים ממה שהיינו רוצים (אפשר לשים נקודת עצירה או אפילו הדפסת console.log)

  2. לממש בעצמנו את shouldComponentUpdate ובעזרת נקודת עצירה להבין בדיוק מה אנחנו רוצים להשוות ולהגיע לתנאים הנכונים

  3. אם גילינו שהמימוש שלנו מבצע Shallow Compare - אפשר למחוק את הפונקציה שכתבנו ולהחליף את היררכיית הירושה כך שהפקד יירש מ PureComponent (לא חייבים).

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

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

class Item extends React.PureComponent {
  render() {
    const cls = this.props.winner ? 'red box' : 'grey box';
    return <div className={cls} onClick={this.props.onClick} />
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { winner: 4 };
  }

  shuffle() {
    this.setState({
      winner: _.random(10),
    })
  }

  render() {
    return (
      <div>
        {_.range(10).map(i => (
          <Item winner={this.state.winner == i} onClick={() => this.shuffle()} />
        ))}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.querySelector('main'));

קודם כל נרצה לשאול - בעת לחיצה על אחד הכפתורים (שגורמת להזזה של הריבוע האדום); כמה render-ים מבוצעים? דרך קלה לענות על זה היא להוסיף פקודת console.count בתוך פונקציית ה render של Item. כלומר הקומפוננטה תיראה כך:

class Item extends React.PureComponent {
  render() {
    console.count('Item::render');
    const cls = this.props.winner ? 'red box' : 'grey box';
    return <div className={cls} onClick={this.props.onClick} />
  }
}

התוצאה הלא מפתיעה היא שאחרי כל לחיצה יש לנו 10 הודעות:

console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 1
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 2
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 3
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 4
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 5
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 6
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 7
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 8
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 9
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 10

זה בטח לא דבר טוב - היינו מעדיפים שהפונקציה render תיקרא רק פעמיים: אחת עבור הריבוע שהופך מאפור לאדום והשניה עבור הריבוע שהופך מאדום לאפור. מאחר והריבועים יורשים כולם מ PureComponent זה אומר שאחד השדות ב props או ב state יצא שונה בכל אחד מהריבועים. בשביל לגלות איזה שדה זה נוסיף מימוש משלנו עם הודעות הדפסה מתאימות:

class Item extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    const sameWinner = nextProps.winner === this.props.winner;
    const sameOnClick = nextProps.onClick === this.props.onClick;
    console.log('<<<<<- ');
    console.log('Same class: ', sameWinner);
    console.log('Same on click: ', sameOnClick);
    console.log('>>>>>- ');
    return !(sameWinner && sameOnClick);
  }

  render() {
    console.count('Item::render');
    const cls = this.props.winner ? 'red box' : 'grey box';
    return <div className={cls} onClick={this.props.onClick} />
  }
}

והתוצאה:

<<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 1
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 2
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 3
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 4
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 5
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 6
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 7
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 8
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 9
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 <<<<<- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same class:  true
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Same on click:  false
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 >>>>>- 
console_runner-1df7d3399bdc1f40995a35209755dcfd8c7547da127f6469fd81e5fba982f6af.js:1 Item::render: 10

ואכן אנחנו רואים בהדפסה שרק הריבועים שצריכים להשתנות קיבלו ערכים אחרים כשאנחנו משווים את props.winner, אבל בהשוואת props.onClick כל ריבוע קיבל ערך אחר בכל render. הסיבה נעוצה בשורה:

          <Item winner={this.state.winner == i} onClick={() => this.shuffle()} />

בכל פעם שאנחנו מפעילים את ה render של App אנחנו גם יוצרים פונקציית onClick חדשה! ולכן כל render של Item מקבל בתור Property את ה onClick החדשה, ההשוואה מחזירה שיש הבדל ב Property זה וכך מגיעים לקריאות מיותרות ל render. דרך קלה לתקן את זה אחרי שהבנו את הבעיה היא להעביר את ה bind לבנאי כך שפקד App יראה כך:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { winner: 4 };
    this.shuffle = this.shuffle.bind(this);
  }

  shuffle() {
    this.setState({
      winner: _.random(10),
    })
  }

  render() {
    return (
      <div>
        {_.range(10).map(i => (
          <Item winner={this.state.winner == i} onClick={this.shuffle} />
        ))}
      </div>
    );
  }
}

ואכן אחרי השינוי כל לחיצה מייצרת רק שתי הדפסות Item::render במקום 10. הקוד המתוקן המלא נמצא בקודפן הזה:

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