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

מה כל Hook עושה באמת

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

useRef, לעומת זאת, אינו אלא אובייקט JavaScript פשוט עם מאפיין current. React מבטיחה להעביר לך את אותה הפניה (reference) לאותו אובייקט בדיוק בכל render. היא לא עוקבת אחרי מה שיש בתוכו. שינוי (mutation) של someRef.current קורה בשקט. React לא תגיב.

השקט הזה הוא בדיוק הנקודה. Refs הם "פתח מילוט" (escape hatch), לא תחליף ל-state.

ההפרדה ב-Rendering

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

אם אתה משנה ref, שום דבר לא קורה מבחינה ויזואלית. המשתנה מתעדכן באופן מיידי וסינכרוני, אך הקומפוננטה אינה עוברת re-render. זה הופך את ה-refs לאידיאליים עבור ערכים שתומכים בעבודת הפנים של הקומפוננטה מבלי להיות חלק מהפלט הוויזואלי. חשוב על timer IDs, snapshots של props קודמים, או ישירות על DOM handles. ל-UI לא אכפת מה-interval ID שמפעיל autoplay של קרוסלה; אכפת לו רק מאיזה slide גלוי. ה-interval ID שייך ל-ref.

מתי state הוא הכלי הנכון

השתמש ב-useState בכל פעם שערך הוא חלק מהפנים (surface) של ה-UI שלך.

שדות קלט (input fields) הם הדוגמה הברורה ביותר. אם משתמש מקליד כתובת אימייל ואתה צריך לתקף אותה ולהציג הודעת שגיאה מתחת לתיבה, מחרוזת האימייל הזו צריכה state. לוגיקת התיקוף ובאנר השגיאה תלויים שניהם בערך העדכני ביותר, ו-React יודעת לעדכן את הבאנר רק בגלל שה-state גרם ל-re-render.

מונים (counts) וטוגלים (toggles) הם עוד מרכיב בסיסי. כפתור שמעלה ניקוד, דגל (flag) לפתיחה/סגירה של מודאל, אינדקס של טאב—כל אלו עוברים דרך state כי פלט ה-render משתנה יחד עם הערך. אפילו ערכים נגזרים (derived values), כמו רשימה מסוננת התלויה במחרוזת חיפוש, מתחילים בדרך כלל ב-state כי ערך המקור גלוי למשתמש.

יש גם דקויות של תזמון שכדאי להבין. עדכוני state הם אסינכרוניים ומקובצים (batched). אם אתה קורא ל-setCount(count + 1) שלוש פעמים בתוך event handler אחד, React לא מבצעת render שלוש פעמים. היא מקבצת אותם לעדכון יחיד. גם המשתנה count בתוך הפונקציה הרצה נשאר "ישן" (stale) עד ל-render הבא. הקיבוץ הזה הוא פיצ'ר. הוא שומר על אפליקציות מהירות. אך המשמעות היא שאינך יכול לצפות שמשתנה ה-state ישקף את הערך החדש כבר בשורה הבאה.

מתי refs מצילים את המצב

השתמש ב-useRef עבור "צנרת" (plumbing), לא