לדלג לתוכן

אנטי-פטרנים נפוצים בריאקט - מה כדאי להפסיק לעשות

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

העברת props מיותרת דרך קומפוננטות - Prop Drilling

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

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

מוטציה ישירה של state

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

הכלל הפשוט: תמיד יוצרים עותק חדש. במקום array.push(item), בונים מערך חדש עם הפריט הנוסף. במקום obj.name = "חדש", בונים אובייקט חדש עם השדה המעודכן. זה נשמע כמו פרט טכני קטן, אבל זה אחד המקורות הנפוצים ביותר לבאגים "מסתוריים" של "למה הממשק לא מתעדכן".

מפתח לפי אינדקס במערך

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

שליפת נתונים ב-useEffect בלי ניקוי

תבנית נפוצה היא לשלוף נתונים בתוך useEffect כשקומפוננטה עולה. הבעיה מתחילה כשהמשתמש מנווט הלאה מהר, או כשה-props שהבקשה תלויה בהם משתנים לפני שהבקשה הקודמת חזרה. בלי מנגנון ביטול או "התעלמות" מתשובות ישנות, אתם עלולים לקבל מצב שבו תשובה איטית מבקשה ישנה מגיעה אחרי תשובה מהירה מבקשה חדשה, ופתאום המסך מציג נתונים לא רלוונטיים. גרוע מזה - אם הקומפוננטה כבר הוסרה מה-DOM עד שהתשובה חזרה, וקוראים ל-setState עליה בכל זאת, זו שגיאה קלאסית ומעצבנת לדבג.

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

קומפוננטות ענק ("god components")

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

שימוש יתר ב-state גלובלי

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

לסיכום

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

מזהים אצלכם אנטי-פטרן נוסף שלא הזכרנו? ספרו לנו בדיסקורד.

הצטרפו לקהילה בדיסקורד