טיפול בשגיאות ב React

09/11/2018

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

אחד הפיצ'רים החדשים בריאקט 16 היה מנגנון טיפול בשגיאות חדש. בואו נראה למה היה צריך את השינוי ומה הרווחנו ממנו.

1. טיפול בשגיאות בריאקט 15

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

ניקח לדוגמא את הפקד הבא בריאקט 15:

class DieAfterTen extends React.Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
    this.inc = this.inc.bind(this);
  }

  inc() {
    this.setState(oldState => ({counter: oldState.counter + 1}));
  }

  render() {
    if (this.state.counter > 10) {
      throw 'Ouch';
    }
    return (
      <button onClick={this.inc}>{this.state.counter}</button>
    )
  }
}

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

אז נכון אם אתם כותבים את הפקד DieAfterTen כנראה עשיתם משהו ממש רע בזה שזרקתם Exception ואתם לא אמורים להתנהג כך. אבל מה אם התפקיד שלכם זה לכתוב את App, כלומר הפקד שמשתמש ב DieAfterTen? מסתבר שכאן יש לכם הרבה פחות אופציות. אם השתמשתם בפקד שזורק Exception מתוך ה render שלו, אין לכם אפילו איפה לתפוס את ה Exception הזה.

2. הפיתרון של ריאקט 16

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

לכן ריאקט 16 הוסיפו מנגנון שנקרא Error Boundaries. מטרת המנגנון לעטוף פקדים שעלולים לזרוק Exception ב render שלהם ולאפשר לנו לטפל במצב כזה מתוך הפקד שמשתמש בהם.

בדוגמא שלנו נוכל בתוך App להגדיר Error Boundary סביב אחד הכפתורים (או כולם) וכך לתת ממשק חלופי במצב שאחד הכפתורים נשבר. המנגנון מבוסס על שתי פונקציות ואתם יכולים לבחור לממש אחת או את שתיהן:

  1. הראשונה נקראת getDerivedStateFromError. היא מקבלת כפרמטר את אוביקט השגיאה ותפקידה להפוך את השגיאה לשדה ב State. ב render הבא תוכלו לבדוק את הסטייט ולהציג תוכן חלופי לתוכן שנשבר.

  2. הפונקציה השניה נקראת componentDidCatch. היא מופעלת כל פעם שרכיב נשבר ב render ותפקידה לתעד את השגיאה לקונסול או ללוג על השרת.

לכן בשביל לתקן את הכפתור נוכל לעטוף אותו בפקד נוסף שיתפוס את השגיאות מה render של הכפתור ובמקומו יציג תוכן חלופי:

class ButtonThatNeverDies extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
  }

  static getDerivedStateFromError(error) {
    return { error: true };
  }

  render() {
    if (this.state.error) {
      return <span>Boom!</span>
    } else {
      return <DieAfterTen />
    }
  }
}

וזה עובד - הנה אפילו קודפן להוכיח את זה:

3. הבעיה בפיתרון

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

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

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

רוצים לשמוע מה עוד חדש בריאקט 16? בשבוע הבא אעביר וובינר בדיוק על זה. יהיו שם Error Boundaries, Memo, Lazy Loading ועוד המון יכולות חדשות. פרטים והרשמה בחינם בקישור:

https://www.tocode.co.il/workshops/54