גבולות שגיאה

בעבר, שגיאות בתוך קומפוננטות ב-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, info) {
    // אפשר גם לתעד את השגיאה לשירות לוגר
    logErrorToMyService(error, info);
  }

  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.

איפה למקם את גבולות שגיאה

אפשר להגדיר גבולות שגיאה ברמה גבוהה או נמוכה לפי הצורך או לפי ההעדפה אישית. לדוגמא, בשורש הצומת של עץ הקומפוננטות עם ממשק חלופי של ״משהו השתבש..״ (כמו שלרוב מנוהלות התרסקויות בצד השרת), או לחלופין לעטוף כל קומפוננטה או ווידג׳ט בגבול שגיאה אישי כדי למנוע התרסקות של שאר האפליקציה.

התנהגות חדשה לשגיאות שלא נתפסו

גרסה 16 של React מציגה שינוי בהתנהגות עם השלכות חשובות. שגיאות של נתפסות על ידי גבולות שגיאה יביאו לפירוק מוחלט של עץ הקומפוננטות הראשי.

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

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

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

אנחנו ממליצים גם להשתמש בשירותי דיווח השגיאות של JavaScript (או לבנות שירותים דומים בעצמכם), על מנת למצוא בעיות בסביבת הייצור ולתקן אותן בקלות ובמהירות.

מעקבי ערימות לקומפוננטות

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

שיגאה שנתפסה על ידי גבולות שגיאה

אפשר גם לראות את שם הקובץ ומספר השורה בקוד הקומפוננטה בעזרת מעקב הערימות. זה עובד בברירת המחדל בפרויקטים שנוצרו עם אפליקצית 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 כרגיל:

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 <div onClick={this.handleClick}>Click Me</div>
  }
}

שימו לב שהדוגמא הנ״ל מדגימה קוד JavaScript סטנדרטי לטיפול בשגיאות ולא קשורה בשום אופן לגבולות שגיאות.

שינוי שם מגרסה 15

גרסה 15 של React כללה תמיכה מוגבלת ביותר לגבולות שגיאות תחת מתודה בשם אחר: unstable_handleError. המתודה הזאת כבר לא נתמכת, ותאלצו לשנות אותה ל- componentDidCatch בקוד שלכם החל מגרסת הביתא הראשונה של React 16.

בשביל השינוי הזה, כללנו משנה קוד (codemod) כדי לעדכן באופן אוטומטי את הקוד הרלוונטי.