אפליקציות React בנויות מרכיבים (components), ולכל רכיב יש מחזור חיים משלו. הוא מופיע על המסך. הוא מגיב למידע חדש. בסופו של דבר, הוא נעלם. אם תתעלמו מהקצב הזה, המחיר יהיה אמיתי. דליפות זיכרון (memory leaks) יתחילו להופיע. ביצועי הרינדור (render performance) ידרדרו. משתמשים ילחצו על כפתורים שכבר לא מגיבים, כי הרכיב שהיה מחובר אליהם הוסר מהעץ (unmounted) לפני עשר דקות, אך מאזין האירועים (event listener) עדיין חי. הבנת מחזור החיים של הרכיב אינה תרגיל אקדמי. היא הבסיס לכתיבת תוכנה שנותרת אמינה תחת שימוש אמיתי.

חשבו על קיומו של רכיב בשלושה שלבים. מפתחים מתארים אותם לעיתים קרובות כלידה, צמיחה ומוות. React מכנה אותם mounting, updating ו-unmounting. לכל שלב יש תפקיד ספציפי, וכל אחד מהם מעניק לכם רגע מדויק להריץ קוד או לנקות אותו.

שלושת השלבים

Mounting הוא הלידה. הרכיב נוצר ומוכנס ל-DOM של הדפדפן בפעם הראשונה. זהו חלון ההגדרה (setup) שלכם. אם הרכיב זקוק לנתונים מרחוק, זה הזמן לבקש אותם. אם הוא זקוק למצב התחלתי (initial state) המחושב מתוך props, תעשו זאת כאן. בעידן של רכיבי מחלקה (class components), זה היה התחום של componentDidMount. עם רכיבים פונקציונליים מודרניים, אתם מטפלים בכך באמצעות useEffect ומערך תלויות ריק (empty dependency array). הפרט המכריע הוא שבלוק הקוד הזה רץ פעם אחת, מיד לאחר ש-React מציבה את האלמנט בדף. זה הופך אותו לאידיאלי להגדרה חד-פעמית כמו שליפת פרופיל משתמש או קריאת ערך מ-localStorage.

Updating הוא הצמיחה. רכיב שעבר mounting לא נשאר סטטי. props מגיעים מרכיב אב. ה-state משתנה כתוצאה מאינטראקציית משתמש. האב עצמו מבצע רינדור מחדש (re-render), מה שמאלץ את הילד לעקוב. React מבצעת אז רינדור מחדש לרכיב כדי לשמור על ממשק המשתמש (UI) מסונכרן עם הנתונים שלכם. שלב זה חוזר על עצמו פעמים רבות במהלך חיי הרכיב. בכל פעם שתלות (dependency) משתנה, ה-effects שלכם רצים שוב, והרכיב מחזיר פלט חדש ל-DOM. מכיוון ששלב זה מופעל בתדירות גבוהה כל כך, גם כאן מתחילות רוב בעיות הביצועים. עבודה מיותרת בשלב זה מצטברת במהירות.

Unmounting הוא המוות. הרכיב עוזב את המסך, אולי כי המשתמש עבר לדף אחר או שרינדור מותנה (conditional render) כיבה אותו. בנקודה זו, הרכיב מושמד, אך העולם החיצון לא תמיד יודע זאת. טיימרים ממשיכים לפעול. WebSockets ממשיכים להקשיב. מאזיני אירועים (event listeners) על אובייקט ה-window נשארים מחוברים. אם לא תנקו אותם, הם יחזיקו הפניות לרכיבים מתים ויגרמו לדליפות זיכרון. הדפדפן מבזבז מחזורי עיבוד על "רוחות רפאים". React נותנת לכם פונקציית ניקוי (cleanup function) בדיוק כדי שתוכלו לעצור אינטרוולים, לסגור חיבורים ולהסיר מאזינים לפני שהרכיב נעלם לתמיד.

ממתודות מחלקה ל-Hooks

במשך שנים, מהנדסי React ניהלו את השלבים הללו באמצעות רכיבי מחלקה (class components). הלוגיקה פוצלה בין מתודות נפרדות. שלפתם נתונים בתוך componentDidMount, הגבתם לשינויי props בתוך componentDidUpdate, וניקיתם את ה"רצפה" בתוך componentWillUnmount. הבעיה הייתה שהלוגיקה הקשורה הסתיימה מפוזרת. פיצ'ר בודד, כמו חיבור לצ'אט חי, דרש קוד בשלוש מתודות מחזור חיים נפרדות רק כדי להתחבר, לבדוק שינויים בשרשור (thread) ולהתנתק.

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

כדי לטפל ב-mounting, כתבו useEffect(() => { ... }, []). מערך התלויות הריק אומר ל-React של-effect הזה אין תלויות חיצוניות, ולכן הוא ירוץ רק לאחר הרינדור הראשוני. זה מחליף את componentDidMount.

כדי לטפל ב-updates, כללו את הערכים הספציפיים ש-React צריכה לעקוב אחריהם: useEffect(() => { ... }, [dependency]). כאשר dependency משתנה בין רינדורים,