[ הרצאה · DOTB Meetup #1 · The Beginning ]

למה כולם מדברים על Recoil?

וידאו · 28 באפריל 2023 · 25:04 · מיכל גיגי

דאשבורד דינמי לא צריך לרנדר את כל המסך בכל שינוי. מיכל גיגי מפרקת את Recoil, atoms ו-selectors לניהול state מדויק ב-React, בלי props drilling ומקור אמת כפול.

צפו בהרצאה · 25:04
תמונה ממוזערת של ההרצאה: למה כולם מדברים על Recoil?
[ כתבת ההרצאה ]

הסיפור שמאחורי ההרצאה

[ תקציר מהיר ]
  • מיכל גיגי מציגה בעיית state מדשבורד דינמי ב-Skai: רשימות לא ידועות מראש, שינויים מקומיים ושיתוף נתונים בין רכיבים ברמות שונות.
  • Redux, props ו-React Context נבחנו, אבל כל אחד מהם הוסיף חיכוך למבנה הזה - boilerplate, props drilling או ניהול מסובך של providers דינמיים.
  • ב-Recoil, atom הוא יחידת state עצמאית. רק רכיבים שנרשמו ל-atom שהשתנה מתרנדרים מחדש.
  • Selectors מוסיפים שכבת לוגיקה מחושבת על atoms או selectors אחרים, כולל פעולות אסינכרוניות והבאת מידע מהשרת.
  • Recoil אינו ברירת מחדל לכל אפליקציה: מתחילים בכלים המובנים של React, ובוחרים בו כשה-state הופך מורכב, דינמי או קשה לתחזוקה.

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

מיכל גיגי, Frontend Engineer ב-Skai, לוקחת אותנו לבעיה אמיתית: תשתית שמאפשרת ללקוחות להרכיב dashboards דינמיים מהנתונים שלהם. יש pages, קבוצות של widgets, ורשימות שאי אפשר לדעת מראש כמה פריטים יהיו בהן. יש פילטרים ברמת דף, עריכה מתוך widget, וגם פעולות מחוץ לו שצריכות להשפיע עליו. במבנה כזה, השאלה אינה רק איפה שומרים state. השאלה היא איך שומרים מקור אמת אחד בלי לגרום לכל האפליקציה לרוץ בכל שינוי.

הבעיה של dashboard דינמי אינה רק state גלובלי

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

במקביל, אותו נתון יכול להשתנות משני כיוונים: בתוך ה-widget ומחוץ לו. אם מחזיקים state מקומי בתוך הרכיב וגם state חיצוני עבור אותו sort, נוצרת כפילות. עכשיו צריך לסנכרן בין שני מקורות. זה הרגע שבו דברים מתחילים להישבר.

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

מיכל מגדירה שלוש דרישות חדות: מקור נתונים יחיד שנגיש בין רכיבים בכל רמה, רינדור רק של הרכיבים הרלוונטיים בכל שינוי, ותמיכה ברשימות דינמיות שאפשר להוסיף ולמחוק. זו מסגרת טובה לכל החלטה על ניהול state ב-React. לא “איזו ספרייה הכי פופולרית”, אלא איזה מודל מחזיק את הדרישות בלי להעמיס על הקוד.

למה Redux, props ו-React Context לא סגרו את הפינה

Redux יודע לשתף מידע בין קומפוננטות, אבל במקרה שמיכל מתארת הוא הכניס יותר מדי boilerplate. לכל פרמטר צריך store, reducer ו-action. כשיש הרבה יחידות state קטנות ודינמיות, המחיר הוא לא רק כמות קוד. קשה יותר לראות מה קשור למה.

props מרגישים טבעיים ב-React. אפשר להעביר נתונים במורד העץ. אבל כשדאשבורד עמוק ויש הרבה פרמטרים, זה הופך ל-props drilling. מעבירים state דרך רכיבים שלא משתמשים בו רק כדי שיגיע לרכיב שכן צריך אותו. ואם כל הדאשבורד נשען על אובייקט state אחד, שינוי ב-widget אחד עלול לגרור רינדור של כל ה-widgets.

גם React Context פותר חלק מהבעיה: הוא מאפשר לשתף state בלי להעביר props. מיכל מציינת שאפשר אפילו להקים provider לכל widget כדי לבודד רינדורים. אבל כמות ה-widgets אינה ידועה מראש, והמערכת צריכה לאפשר הוספה ומחיקה שלהם. ניהול דינמי של providers הופך את הפתרון למסובך יותר מהבעיה.

הלקח אינו ש-Redux או Context גרועים. הם פשוט לא היו ה-tradeoff הנכון לתשתית הזו. state management טוב מתחיל מהצורה שבה הנתונים זזים באפליקציה, לא מהספרייה שכבר מכירים.

Recoil מפרק את ה-state ליחידות שאפשר להירשם אליהן

Recoil היא ספרייה לניהול state ב-React. בהרצאה מיכל מציגה את המודל שלה דרך atoms ו-selectors. atom הוא יחידת state עצמאית - מקור אמת שאינו צריך להיות קבור בתוך רכיב יחיד. רכיב יכול להירשם ל-atom, לקרוא אותו ולשנות אותו.

כשה-atom משתנה, רק הרכיבים שנרשמו אליו מתעדכנים. זאת הנקודה החשובה בדאשבורד: לא מעדכנים מסך שלם בגלל שה-sort של טבלה אחת השתנה. בונים גבולות סביב ה-state עצמו, והרינדור עוקב אחריהם.

כל פעם שהוא ישתנה הקומפוננטות שנרשמו אליו יתרנדרו ורק הן יתרנדרו.

מעל atoms יושבים selectors. אלה פונקציות מחושבות שמתבססות על atoms או על selectors אחרים. אפשר לשים שם לוגיקה של state, לחשב ערך נגזר, לסנן רשימה ואפילו לבצע עבודה אסינכרונית כמו הבאת מידע מהשרת. בדוגמה של מיכל, selector יכול לסנן רשימה שנשמרת ב-atom. כשהרשימה משתנה, ה-selector מחשב מחדש, ורק הרכיבים שתלויים בו מתרנדרים.

כך זה נראה בדשבורד של Skai

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

לכל widget אפשר להגדיר פונקציה שמקבלת ID ומחזירה atom. ה-ID מאפשר ליצור key ייחודי, וה-atom מחזיק את הפרמטרים שמעניינים את אותו widget, למשל הכותרת או ה-sort, יחד עם ערך ברירת מחדל. בתוך ה-widget, useRecoilState משמש כמו useState, אלא שהפרמטר שלו הוא ה-atom המתאים ל-ID.

התוצאה היא שהגדרות נגישות גם בתוך ה-widget וגם מחוצה לו, בלי לשמור שתי גרסאות של אותו נתון. שינוי שמגיע מ-settings יכול לעדכן את ה-atom. הטבלה שנרשמה אליו מקבלת את הערך החדש ומתרנדרת מחדש. אותה גישה עובדת גם על filters ברמת page: ה-widgets של אותו page נרשמים לאותו atom, ואילו widgets ב-page אחר לא מגיבים לשינוי.

snapshots הם לא קישוט - הם כלי להבנת state

מיכל מציגה גם snapshots: תמונת מצב של ה-state בנקודת זמן, כולל הערכים של atoms. אפשר להאזין לשינויים ולדעת אילו atoms השתנו. מכאן נפתחות יכולות שימושיות: שמירת היסטוריית שינויים, מימוש undo, routing דרך מזהה state ב-URL, והרצת tests מול snapshot מסוים.

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

לא כל אפליקציית React צריכה Recoil

החלק החשוב בהרצאה הוא דווקא הבלם. מיכל לא מציעה להחליף כל useState ב-Recoil. בהרבה מקרים props בשילוב React Context עובדים מצוין. בטפסים, למשל, אפשר להשתמש ב-Formik, שמטפל גם בוולידציות, בשגיאות וב-state שנגיש מכל מקום בטופס.

בכל מקום בקוד לא. בהרבה מקרים אפשר להסתדר עם props בשילוב עם react-context.

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

השורה התחתונה

  • dashboard דינמי דורש מקור אמת אחד, אבל לא רינדור גלובלי בכל שינוי.
  • atoms מאפשרים לחלק את ה-state ליחידות ממוקדות ולהירשם רק למה שרלוונטי.
  • selectors מחזיקים state נגזר, לוגיקה ופעולות אסינכרוניות בלי לדחוף הכול לתוך רכיבים.
  • RecoilRoot, atoms לפי ID ו-key ייחודי מתאימים למבנה של widgets דינמיים.
  • מתחילים ביכולות הבסיס של React. כשמבנה הנתונים והרינדור דורשים יותר, Recoil יכול להיות tradeoff נקי יותר.

ההרצאה המלאה נותנת את התרשים, הקוד והדיון מהקהל על ההחלטה. אם אתם בונים dashboard ב-React, סובלים מ-props drilling או מנסים להבין למה שינוי קטן מרנדר יותר מדי רכיבים - היא שווה צפייה.

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

- מיכל גיגי

"כל פעם שהוא ישתנה הקומפוננטות שנרשמו אליו התרנדרו ורק הן התרנדרו."

- מיכל גיגי

"בכל מקום בקוד לא. בהרבה מקרים אפשר להסתדר עם props בשילוב עם react-context."

- מיכל גיגי
[ פרקי ההרצאה ]
  1. 00:01:20
    הבעיה: dashboard דינמי עם state שלא נשאר במקום
    מיכל מתארת את התשתית שנבנתה ב-Skai ואת הדרישות של pages, widget groups ורשימות שמשתנות בזמן ריצה.
  2. 00:06:55
    למה Redux, props ו-Context לא התאימו למקרה
    החלופות מאפשרות שיתוף מידע, אך מביאות איתן boilerplate, props drilling או providers שקשה לנהל כשמספר הווידג'טים דינמי.
  3. 00:09:08
    מהו Recoil ולמה הוא מרגיש Reactי
    מיכל מציגה את Recoil כספריית state ל-React עם API מינימליסטי, שנועדה להישאר קרובה למודל המוכר של React.
  4. 00:10:05
    Atoms ו-selectors: מקור אמת ורינדור ממוקד
    atoms מחזיקים יחידות state; selectors מחשבים ערכים על בסיסן ומאפשרים לוגיקה ופעולות אסינכרוניות.
  5. 00:12:29
    יישום בדשבורד: atom לכל widget
    RecoilRoot עוטף את אזור הדשבורד, ופונקציה לפי ID מייצרת atom עם key ייחודי וערך ברירת מחדל לכל widget.
  6. 00:15:10
    Snapshots, undo, routing ו-DevTools
    מיכל מראה איך snapshot של ה-state יכול לתעד שינויים, לתמוך ב-undo, ב-routing, בבדיקות ובכלי פיתוח.
  7. 00:16:16
    מתי Recoil מתאים ומתי לא
    לא כל state דורש ספרייה חדשה. המקרה מתחיל להיות מתאים כשיש רשימות דינמיות, עץ מורכב או צורך ברינדור סלקטיבי.
[ נושאים ]

מה כוסה בהרצאה

#פרונטאנד #Frontend #React #State
[ שאלות נפוצות ]

שאלות מההרצאה

מהו Recoil ב-React?

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

איך atoms ב-Recoil משפרים רינדור של dashboard?

אפשר להחזיק atom נפרד לכל widget. כש-atom משתנה, רק הרכיבים שנרשמו אליו מתרנדרים מחדש, במקום לרנדר מחדש את כל הווידג'טים בדשבורד.

מה התפקיד של selectors ב-Recoil?

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

למה לא להשתמש פשוט ב-React Context?

Context חוסך העברת props, אבל במבנה עם כמות לא ידועה של widgets והוספה ומחיקה שלהם, צריך גם ליצור ולהסיר providers. מיכל מציגה זאת כסיבוך שלא התאים לתשתית הדשבורד שלה.

מתי כדאי לשקול Recoil?

לפי ההרצאה, לא מתחילים מ-Recoil. קודם בודקים props, React Context ופתרונות ייעודיים כמו Formik לטפסים. Recoil נהיה רלוונטי כשיש רשימות דינמיות, עץ נתונים מורכב או הרבה רכיבים שרק חלקם צריכים להתעדכן.

[ קהילה ]

רוצה לדבר על ההרצאה?

הצטרף לקהילת המפתחים שלנו ב-WhatsApp.