• בלוג
  • עמוד 222
  • בדיוק כשהתרגלנו להבדל בין Functions ל Classes ב React

בדיוק כשהתרגלנו להבדל בין Functions ל Classes ב React

02/11/2018

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

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

1. בעיות עם רכיבים פונקציונאליים

בפוסט בו הודיעו על ריאקט 0.14 הוצגה הדוגמא הבאה לרכיב פונקציונאלי:

var Aquarium = (props) => {
  var fish = getFish(props.species);
  return <Tank>{fish}</Tank>;
};

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

var Aquarium = React.createClass({
    render: function() {
        var fish = getFish(this.props.species);
        return <Tank>{fish}</Tank>;
    },
});

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

2. מה כבר עובד

ריאקט 16.6 שכבר זמינה ועובדת כוללת מנגנון חדש שנקרא memo. המנגנון דומה מאוד ל shouldComponentUpdate ומאפשר "לדלג" על render כאשר לא היה שינוי אמיתי ב props. נראה את זה דרך שלוש דוגמאות.

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

function Hello(props) {
  return (<p>Hello {props.name}</p>);
}

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

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

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

const Hello = React.memo((props) => {
  $debug.text(`Renders: ${++renderCount}`);
  return (<p>Hello {props.name}</p>);
});

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

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

3. מה הלאה

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

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

https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889

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