שימוש נכון בסטייט בריאקט
אז נניח שקיבלתם מטלה ליצור טיימר, אבל בלי להשתמש בעזרי Date, אלא ממש טיימר שאתם מוסיפים בו ידנית שנייה בכל שנייה שעוברת. די פשוט (לכאורה). האינסטינקט הראשוני שלי היה לבנות סטייט של seconds, וליצור אינטרוול שבכל שנייה מגדיל את seconds ב 1:
אבל בפועל, מה שנקבל יהיה 0 בשנייה הראשונה, 1 בשנייה השנייה, 1 בשנייה השלישית, 1 בשנייה הרביעית, וכן הלאה.
ולמה? כי clousure וכי עוד הרבה סיבות.
כאשר אנחנו נותנים לsetInterval את הפונקציה:
() => setSeconds(seconds + 1)
חשוב להבין שכאשר הערך של state משתנה, מה שקורה זה שהפונקציה של הקומפוננטה מתרדנדרת מחדש עם state חדש שמחזיק בערך החדש, ולא חס וחלילה שמחילים שינוי על ה*משתנה* הקודם שהחזיק את הערך. ההוכחה הכי פשוטה לזה היא השימוש בconst בשביל להחזיק את ערך הסטייט, דבר שלא היה מתאפשר אם אכן היינו רוצים לשנות את הערך של *משתנה* זה ישירות.
במקרה שלנו, אנחנו מאזכרים את המשתנה seconds, שהוא לצורך העניין משתנה רגיל לחלוטין שערכו תמיד היה ותמיד יהיה הערך של seconds נכון לרגע הרינדור הראשון של הפונקצייה - 0.
זאת אומרת שבמקרה שלנו, כאשר אנחנו מאזכרים את seconds, אנחנו מאזכרים בעצם את הערך הקבוע 0. כמובן שכאשר נבצע שינוי באותו סטייט, הקומפוננטה תתרנדר מחדש, וייווצר שוב משתנה seconds חדש, הפעם עם הערך החדש. אבל הseconds הקודם עדיין חי ובועט, ומבחינתה של הפונקציה שבתוך האינטרוול, זה הseconds המדובר. אז מה שיקרה זה שבאיטרציה הראשונה באינטרוול, תוצא לפועל הפונקציה:
() => setSeconds(seconds + 1)
כאשר הערך של seconds הוא כמו שאמרנו - 0. זה ישנה את ערך ה*סטייט* ל1 וירנדר מחדש את הקומפוננטה, אבל כמו שדובר, המשתנה seconds מהרינדור הקודם עדיין שווה ל0.
בפעם הבאה יקרה אותו תהליך עם אותו ערך, וכנל עד שהקומפוננטה\אינטרוול ימותו.
אז איך בכל זאת מיישמים את הלוגיקה של טיימר "נייטיבי" בריאקט? בעזרת פיטצ'ר מגניב שפונקציית הset של הסטייט מקבלת:
בשימוש הקלאסי של setState אנחנו פשוט מעבירים את הערך הרצוי החדש כפרמטר, אבל ניתן לראות שיש גם אפשרות ב', והיא להעביר פונקצייה כפרמטר. הפונקצייה הזו תקבל כפרמטר ראשון את הערך *הנוכחי* של *הסטייט*, ומה שהיא תחזיר (AKA return), ישמש כערך החדש של אותו סטייט.