גבולות שגיאה
בעבר, שגיאות בתוך קומפוננטות ב-JavaScript הובילו להשחתת המצב הפנימי של React וגרמו לפליטת שגיאות אניגמטיות ברינדור המסך הבא. מקור הבעיה תמיד נבע משגיאות קודמות בקוד האפליקציה, אבל React לא סיפק דרך לטפל בהם בחן בתוך הקומפוננטות, ולא מצא דרך להתאושש מהם.
גבולות השגיאה
שגיאת JavaScript בחלק מממשק המשתמש לא אמורה לשבור את כל האפליקציה. כדי לפתור את הבעיה למשתמשי React, גרסה 16 מציגה קונספט חדש של ״גבולות שגיאה״.
גבולות שגיאה הם בעצם קומפוננטות שתופסות שגיאות JavaScript שקורות בכל אחד מקומפוננטות הילד שלהן, מתעדות אותן ומציגות ממשק חלופי. במקום להציג את הקומפוננטה השבורה. הן תופסות שגיאות בזמן רינדור, במתודות מחזור חיים ובבנאי הקומפוננטות עבור כל אחת מקומפוננטות הילד שלהן.
הערה
גבולות שגיאה לא תופסים שגיאות ב:
- מטפלי אירועים (מידע נוסף)
- קוד אסינכרוני (לדוגמא
setTimeout
אוrequestAnimationFrame
)- רינדור בצד השרת
- שגיאות שקורות בגבול השגיאה עצמו
קומפוננטת מחלקה הופכת לגבול שגיאה אם היא מגדירה לפחות אחת ממתודות מחזור החיים static getDerivedStateFromError()
או componentDidCatch()
.
המתודה static getDerivedStateFromError()
משמשת לרנדור ממשק חלופי לאחר שגיאה שנתפסה, ו- componentDidCatch()
עוזרת בתיעוד השגיאה.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) { // כדי שהרינדור הבא יציג ממשק חלופי state מעדכנת את ה return { hasError: true }; }
componentDidCatch(error, errorInfo) { // אפשר גם לתעד את השגיאה לשירות לוגר logErrorToMyService(error, errorInfo); }
render() {
if (this.state.hasError) { // מגדירים ממשק חלופי מותאם return <h1>Something went wrong.</h1>; }
return this.props.children;
}
}
השימוש בגבולות שגיאה זהה לשימוש בכל קומפוננטה רגילה:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
גבולות שגיאה עובדים בצורה דומה לבלוק catch {}
ב-JavaScript, אבל בתוך הקומפוננטה. רק קומפוננטות מחלקה יכולות להיות גבולות שגיאה. בפועל, מגדירים בדרך כלל גבול שגיאה אחד ונשתמש בו בצורה אחידה בכל האפליקציה.
שימו לב שגבולות שגיאה תופסים אך ורק שגיאות בקומפוננטות הילד שלהם, ולא בתוך עצמם. אם מתרחשת שגיאה בקוד ה- render
של גבולות השגיאה לדוגמא, השגיאות תעלה לגבול שגיאה הבא מעליה, בדומה להתנהגות של בלוק ה- catch {}
ב-JavaScript.
הדגמה חיה
שימו לב לדוגמא הבאה של הגדרה ושימוש בגבולות שגיאה עם גרסה 16 של React.
איפה למקם את גבולות שגיאה
רמת הפירוט של הגדרת גבולות שגיאה היא לשיקולכם. תוכלו לעטוף קומפוננטות route ברמה העליונה על מנת להציג הודעת ”משהו השתבש“ למשתמש, כמו הצורה שבה מערכות צד-שרת מטפלות בקריסות לעתים קרובות. תוכלו גם לעטוף ווידג’טים אינדיבידואליים בגבולות שגיאה כדי להגן עליהם מהקרסת שאר האפליקציה.
התנהגות חדשה לשגיאות שלא נתפסו
גרסה 16 של React מציגה שינוי בהתנהגות עם השלכות חשובות. שגיאות של נתפסות על ידי גבולות שגיאה יביאו לפירוק מוחלט של עץ הקומפוננטות הראשי.
דנו בהחלטה זו לא מעט, אבל מנסיונינו תמיד כדאי שלא להשתמש בממשק מושחת, אלא להפטר ממנו לגמרי. לדוגמא, באפליקציה כמו מסנג׳ר, להשאיר ממשק שבור בצורה גלויה לעין יכול להוביל לשליחת הודעה לאדם הלא נכון. באותה מידה, באפליקציה שמנהלת כספים עדיף לא להציג כלום מאשר להציג סכום שגוי.
השינוי הזה אומר שכשמשדרגים לגרסה 16, בדרך כלל מגלים שגיאות באפליקציה שעד אז לא שמנו לב אליהן. הוספת גבולות שגיאה מאפשר לנו לספק חווית משתמש טובה יותר בכל מצב.
לדוגמא, המסנג׳ר של Facebook עוטף תוכן מהסרגל הצידי, חלונית המידע, חלונית השיחה ושדה הקלט של ההודעה בגבולות שגיאה נפרדים. במקרה ואחד מהם קורס, האחרים נשארים זמינים ואינטרקטיביים.
אנחנו ממליצים גם להשתמש בשירותי דיווח השגיאות של JavaScript (או לבנות שירותים דומים בעצמכם), על מנת למצוא בעיות בסביבת הייצור ולתקן אותן בקלות ובמהירות.
stack trace לקומפוננטות
בגרסה 16 של React כל השגיאות שקורות בזמן הרינדור בסביבת הפיתוח מודפסות למסוף בדפדפן, אפילו אם האפליקציה בולעת אותם בטעות. בנוסף להודעת השגיאה וה-stack trace של JavaScript, מודפס גם ה-stack trace של הקומפוננטה. עכשיו אפשר לראות בדיוק איפה בקומפוננטה קרתה השגיאה:
אפשר גם לראות את שם הקובץ ומספר השורה בקוד הקומפוננטה בעזרת ה-stack trace. זה עובד בברירת המחדל בפרויקטים שנוצרו עם אפליקצית Create React:
אם לא יצרתם את הפרויקט עם אפליקצית Create React, תוכלו להשתמש בתוסף הזה - הוסיפו אותו לתצורת ה-Babel בפרויקט. שימו לב שהוא מיועד רק לשימוש בסביבת הפיתוח וחובה לנטרל אותו בסביבת הייצור.
הערה
שמות הקומפוננטות שמוצגים במעקב הערימות תלוי בשם שהוגדר במאפיין
Function.name
. אם אתם צריכים לתמוך בדפדפנים או מכשירים ישנים יותר שלא תומכים בזה באופן סטנדרטי (כמו IE 11 למשל), תוכלו להוסיף את המאפיין כ- polyfill שיוכלל ב-bundle האפליקציה, כמוfunction.name-polyfill
. דרך נוספת היא לספק באופן ישיר את המאפייןdisplayName
בכל קומפוננטה.
מה עם בלוק try/catch?
בלוק try/catch
זה מצוין אבל עובד רק בקוד אימפרטיבי (קוד שמשנה את מצב האפליקציה):
try {
showButton();
} catch (error) {
// ...
}
לעומת זאת, קומפוננטות React הן דקלרטיביות ורק מציינות מה צריך לרנדר באפליקציה:
<Button />
גבולות שגיאה משמרים את האופן הדקלרטיבי של React ומספקים התנהגות דומה. לדוגמא, אפילו אם שגיאה צצה במתודת ה-componentDidUpdate
שנגרמה איפשהו עמוק בתוך העץ בתוך setState
, היא תוצף לגבול השגיאה הקרוב ביותר.
מה עם מטפלי אירועים?
גבולות שגיאה לא תופסים שגיאות מתוך מטפלי אירועים.
React לא צריך גבולות שגיאה כדי להתאושש משגיאות במטפלי אירועים. בשונה מהמתודת הרינדור ומתודות מחזור החיים, שגיאות שצצות במטפלי האירועים לא קורות בזמן הרינדור. אז אם צצה שגיאה, React עדיין ידע מה להציג.
כשיש צורך לתפוס שגיאה במטפל אירוע, השתמשו בביטוי ה-try
/ catch
הרגיל של JavaScript:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
try { // קוד שזורק שגיאה } catch (error) { this.setState({ error }); } }
render() {
if (this.state.error) { return <h1>Caught an error.</h1> } return <button onClick={this.handleClick}>Click Me</button> }
}
שימו לב שהדוגמא הנ״ל מדגימה קוד JavaScript סטנדרטי לטיפול בשגיאות ולא קשורה בשום אופן לגבולות שגיאות.
שינוי שם מגרסה 15
גרסה 15 של React כללה תמיכה מוגבלת ביותר לגבולות שגיאות תחת מתודה בשם אחר: unstable_handleError
. המתודה הזאת כבר לא נתמכת, ותאלצו לשנות אותה ל- componentDidCatch
בקוד שלכם החל מגרסת הביתא הראשונה של React 16.
בשביל השינוי הזה, כללנו משנה קוד (codemod) כדי לעדכן באופן אוטומטי את הקוד הרלוונטי.