error boundary הוא קומפוננטת React שתופסת שגיאות שנזרקות בזמן רינדור הקומפוננטות שבתוכה ומציגה ממשק fallback במקום להפיל את כל האפליקציה. הוא חייב להיות קומפוננטת class שמגדירה static getDerivedStateFromError, כי אין לזה הוק. כותבים אותו פעם אחת ועוטפים בו כל חלק בעץ.
לחצו על "Break the profile" כדי לגרום לילד לזרוק שגיאה בזמן הרינדור.
הפרופיל מוחלף ב-fallback, ושאר הדף ממשיך לעבוד. הקונסול מציג את השגיאה פעמיים: React רושמת כל שגיאה שנתפסה עם console.error, ו-componentDidCatch רושם אותה שוב. "Try again" מנקה את הגורם (broken) ואת השגיאה של ה-boundary, כך שהפרופיל מתרנדר שוב. הסירו את this.props.onReset(); מ-reset ו-Try again זורק ישר בחזרה ל-fallback, כי שום דבר לא תיקן את הגורם.
איך ה-class עובד
שתי מתודות הופכות class ל-error boundary:
static getDerivedStateFromError(error)רצה בזמן הרינדור, מיד אחרי שילד זורק שגיאה. היא מחזירה את עדכון ה-state שמעביר את ה-boundary ל-fallback שלו. היא חייבת להיות טהורה: בלי לוגים, בלי בקשות.componentDidCatch(error, info)רצה אחרי שה-fallback על המסך. השתמשו בה לתופעות לוואי כמו שליחת השגיאה לשירות דיווח.info.componentStackמפרט את הקומפוננטה שזרקה ואת הקומפוננטות שמעליה.
class ErrorBoundary extends Component {
state = { error: null };
static getDerivedStateFromError(error) {
return { error }; // switch to the fallback
}
componentDidCatch(error, info) {
reportError(error, info.componentStack); // your logging service
}
render() {
if (this.state.error) return this.props.fallback;
return this.props.children;
}
}
אין לזה מקבילה בהוקים, ולכן גם אפליקציה שכתובה רק עם קומפוננטות פונקציה צריכה את ה-class האחד הזה. הדף על קומפוננטות class מסביר את תחביר ה-class אם הוא חדש לכם.
מה boundary תופס, ומה לא
boundary תופס שגיאות שקורות בזמן ש-React עובדת על הקומפוננטות שבתוכו:
- רינדור (הגוף של קומפוננטת פונקציה,
renderשל class), - מתודות מחזור חיים ו-constructors של קומפוננטות class ילדות,
- אפקטים (
useEffect,useLayoutEffect) ו-ref callbacks, - promise שנכשל ונקרא עם use, וב-React 19 שגיאה שנזרקת על ידי form action או על ידי פונקציה שמועברת ל-
startTransitionמ-useTransition.
הוא לא תופס:
- שגיאות ב-event handlers,
- קוד אסינכרוני שאתם מתחילים בעצמכם:
setTimeout, callbacks של.then(),awaitב-event handler, - שגיאות ב-
renderשל ה-boundary עצמו (ה-boundary הבא למעלה תופס אותן), - שגיאות בזמן רינדור בצד השרת.
event handlers לא נכללים כי הם לא שוברים את הרינדור. המסך עדיין במצב תקין, ולכן try/catch ב-handler מספיק. אם בכל זאת רוצים שה-boundary יוצג, שמרו את השגיאה ב-state וזרקו אותה ברינדור הבא:
הכפתור הראשון משאיר את הכפתורים על המסך ורק מדפיס שגיאה שלא נתפסה בקונסול. השני תופס את הכישלון, שומר אותו, וה-boundary משתלט.
איפה לשים boundaries
boundary אחד סביב כל האפליקציה עוצר את המסך הלבן, אבל אז כל שגיאה קטנה מחליפה את הכל. שימו boundaries סביב חלקים שיכולים להיכשל באופן עצמאי: widget בסרגל צד, גרף, כל route. כישלון עולה אז למשתמש פאנל אחד, לא את הדף.
רק פאנל ה-Stocks מציג את ה-fallback שלו. העבירו את שני ה-widgets ל-<ErrorBoundary> אחד ופאנל ה-Weather שעובד נעלם יחד איתו.
בלי שום boundary, React מסירה את כל העץ מהמסך כששגיאת רינדור לא נתפסת. דף ריק בטוח יותר מדף שבור שמציג נתונים שגויים, אבל משתמשים קוראים אותו כקריסה.
הספרייה react-error-boundary
החבילה react-error-boundary עוטפת את ה-class בשבילכם ומוסיפה עוזרים לאיפוס. היא לא זמינה בעורך הזה, אז הנה היא בבלוק סטטי:
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';
function Fallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>Something went wrong: {error.message}</p>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
<ErrorBoundary FallbackComponent={Fallback} onReset={() => refetch()} resetKeys={[userId]}>
<Profile userId={userId} />
</ErrorBoundary>;
// Inside a child: hand an async error to the boundary
const { showBoundary } = useErrorBoundary();
fetchData().catch(showBoundary);
resetKeys מאפס את ה-boundary כשאחד מהערכים ברשימה משתנה, ו-showBoundary עושה את טריק ה-"לשמור ולזרוק" מהדוגמה הקודמת.
דיווח על שגיאות ב-React 19
React 19 מדווחת על שגיאות בדרך חדשה. שגיאות שנתפסות על ידי boundary נרשמות פעם אחת עם console.error, ושגיאות שלא נתפסו הולכות ל-window.reportError. כדי לשלוח אותן לשירות משלכם, העבירו אפשרויות ל-createRoot. כל אפשרות מחליפה את הרישום המתאים של ברירת המחדל:
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'), {
onCaughtError(error, errorInfo) {
// an error boundary caught it
logToService('caught', error, errorInfo.componentStack);
},
onUncaughtError(error, errorInfo) {
// no boundary caught it, and React removed the tree
logToService('uncaught', error, errorInfo.componentStack);
},
onRecoverableError(error, errorInfo) {
// React recovered by itself, for example after a hydration mismatch
logToService('recovered', error, errorInfo.componentStack);
},
});
root.render(<App />);
onRecoverableError מיועד לשגיאות ש-React תיקנה בלי העזרה שלכם, כמו hydration mismatch או שגיאת רינדור שנעלמה כש-React ניסתה שוב. hydrateRoot מקבל את אותן שלוש אפשרויות.
טעויות נפוצות
לשים את ה-boundary בתוך הקומפוננטה שזורקת. boundary תופס רק שגיאות מהילדים שלו. אם Profile מרנדרת <ErrorBoundary> סביב ה-JSX שלה ואז זורקת בגוף שלה, השגיאה עוברת אותו אל ה-boundary הבא למעלה. עטפו את הקומפוננטה מההורה שלה.
תופעות לוואי ב-getDerivedStateFromError. היא רצה בזמן הרינדור ועשויה לרוץ יותר מפעם אחת, אז הגבילו אותה להחזרת state. לוגים שייכים ל-componentDidCatch או ל-onCaughtError של ה-root.
איפוס שלא מתקן כלום. ניקוי השגיאה של ה-boundary מרנדר את אותם ילדים עם אותם props. אם הם זורקים שוב, המשתמש חוזר ל-fallback. שנו קודם את מה שגרם לשגיאה (טעינה מחדש, ניקוי קלט שגוי, שינוי ה-key), ואז נקו את השגיאה, כמו שהדוגמה הראשונה עושה עם onReset.
boundary אחד לכל האפליקציה, ותו לא. הוא מונע את הדף הריק אבל הופך כל כישלון קטן לשגיאה בכל הדף. הוסיפו boundaries סביב פיצ'רים, ושמרו את זה ברמה העליונה כמוצא אחרון.
fallbacks שמסתירים מכם את השגיאה. הודעה ידידותית נכונה למשתמשים, אבל ודאו שהשגיאה עדיין מגיעה ללוגים שלכם. React 19 רושמת שגיאות שנתפסו לקונסול כברירת מחדל; בפרודקשן הקונסול הזה הוא של המשתמש שלכם, אז שלחו שגיאות לשירות מ-componentDidCatch או מ-onCaughtError.
error boundaries משתלבים באופן טבעי עם <Suspense>: Suspense מטפל ב-"עוד לא מוכן", ה-boundary מטפל ב-"נכשל". שימו את ה-boundary מחוץ ל-Suspense, כמו בדוגמאות של use, כך שקומפוננטה אחת מכסה טעינה, הצלחה וכישלון.
שאלות נפוצות
מה זה error boundary ב-React?
קומפוננטה שתופסת שגיאות JavaScript שנזרקות בזמן רינדור של כל קומפוננטה שבתוכה, ומרנדרת במקום ממשק fallback. בלי אחד כזה, שגיאה בזמן רינדור מסירה את כל האפליקציה מהמסך.
יש הוק ל-error boundaries?
לא. error boundary חייב להיות קומפוננטת class עם static getDerivedStateFromError (ואופציונלית componentDidCatch). כותבים אותו פעם אחת ומשתמשים בו מקומפוננטות פונקציה כמו בכל קומפוננטה אחרת, או מתקינים את react-error-boundary, שעוטפת את ה-class בשבילכם.
האם error boundaries תופסים שגיאות ב-event handlers?
לא. event handlers רצים מחוץ לרינדור, כך ש-React לא צריכה boundary כדי להתאושש. השתמשו ב-try/catch ב-handler, ואם רוצים שה-boundary יוצג, שמרו את השגיאה ב-state וזרקו אותה בזמן הרינדור.
האם error boundaries תופסים שגיאות אסינכרוניות?
לא מ-setTimeout, מ-callbacks של promises או מ-fetch().then() שאתם מתחילים בעצמכם. הם כן תופסים promise שנכשל ונקרא עם use, וב-React 19 שגיאה שנזרקת בתוך form action או בתוך פונקציה שמועברת ל-startTransition מ-useTransition.
איך מאפסים error boundary?
מנקים את מצב השגיאה שלו, בדרך כלל מכפתור Try again ב-fallback, אחרי שמתקנים את מה שגרם לשגיאה. גם שינוי ה-key של ה-boundary מאפס אותו, כי React עושה mount למופע חדש.