[{"data":1,"prerenderedAt":5969},["ShallowReactive",2],{"page-\u002Freact\u002F16-error-boundaries":3},{"id":4,"title":5,"body":6,"description":5962,"extension":5963,"meta":5964,"navigation":64,"path":5965,"seo":5966,"stem":5967,"__hash__":5968},"content\u002Freact\u002F16-error-boundaries.md","16 — Error Boundaries",{"type":7,"value":8,"toc":5936},"minimark",[9,13,18,119,123,242,246,610,621,776,780,1049,1053,1578,1582,1867,1871,2215,2219,2670,2674,2956,2960,3360,3364,3869,3876,4290,4294,4877,4881,5038,5042,5223,5227,5230,5455,5768,5772,5932],[10,11,5],"h1",{"id":12},"_16-error-boundaries",[14,15,17],"h2",{"id":16},"why-error-boundaries-must-be-class-components","Why Error Boundaries Must Be Class Components",[19,20,23],"code-wrapper",{"filename":21,"language":22},"why_class.js","javascript",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F Error boundaries are the ONE deliberate exception to an all-function-components\n\u002F\u002F codebase. As of today's React, there is NO hook equivalent because the\n\u002F\u002F underlying lifecycle methods (getDerivedStateFromError, componentDidCatch)\n\u002F\u002F have no function-component counterpart.\n\n\u002F\u002F REASON: error boundaries work by React calling a lifecycle method WHEN an\n\u002F\u002F error is thrown during a child's render. Hooks don't have a \"catch errors from\n\u002F\u002F children\" mechanism — useEffect, useLayoutEffect, etc. run AFTER render, not\n\u002F\u002F as an error interceptor DURING the render phase. The class lifecycle is\n\u002F\u002F structurally the only place React can intercept a render-phase error.\n\n\u002F\u002F This is not a temporary gap — it's architectural. A future React version may\n\u002F\u002F provide a function-component equivalent, but none exists today (React 19\n\u002F\u002F included). Every error boundary in production is a class component.\n","",[30,31,32,41,47,53,59,66,72,78,84,90,96,101,107,113],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F Error boundaries are the ONE deliberate exception to an all-function-components\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F codebase. As of today's React, there is NO hook equivalent because the\n",[33,48,50],{"class":35,"line":49},3,[33,51,52],{"class":39},"\u002F\u002F underlying lifecycle methods (getDerivedStateFromError, componentDidCatch)\n",[33,54,56],{"class":35,"line":55},4,[33,57,58],{"class":39},"\u002F\u002F have no function-component counterpart.\n",[33,60,62],{"class":35,"line":61},5,[33,63,65],{"emptyLinePlaceholder":64},true,"\n",[33,67,69],{"class":35,"line":68},6,[33,70,71],{"class":39},"\u002F\u002F REASON: error boundaries work by React calling a lifecycle method WHEN an\n",[33,73,75],{"class":35,"line":74},7,[33,76,77],{"class":39},"\u002F\u002F error is thrown during a child's render. Hooks don't have a \"catch errors from\n",[33,79,81],{"class":35,"line":80},8,[33,82,83],{"class":39},"\u002F\u002F children\" mechanism — useEffect, useLayoutEffect, etc. run AFTER render, not\n",[33,85,87],{"class":35,"line":86},9,[33,88,89],{"class":39},"\u002F\u002F as an error interceptor DURING the render phase. The class lifecycle is\n",[33,91,93],{"class":35,"line":92},10,[33,94,95],{"class":39},"\u002F\u002F structurally the only place React can intercept a render-phase error.\n",[33,97,99],{"class":35,"line":98},11,[33,100,65],{"emptyLinePlaceholder":64},[33,102,104],{"class":35,"line":103},12,[33,105,106],{"class":39},"\u002F\u002F This is not a temporary gap — it's architectural. A future React version may\n",[33,108,110],{"class":35,"line":109},13,[33,111,112],{"class":39},"\u002F\u002F provide a function-component equivalent, but none exists today (React 19\n",[33,114,116],{"class":35,"line":115},14,[33,117,118],{"class":39},"\u002F\u002F included). Every error boundary in production is a class component.\n",[14,120,122],{"id":121},"what-happens-without-an-error-boundary","What Happens Without an Error Boundary",[19,124,126],{"filename":125,"language":22},"without_boundary.js",[24,127,129],{"className":26,"code":128,"language":22,"meta":28,"style":28},"\u002F\u002F By default, an uncaught error thrown during rendering, in a lifecycle method,\n\u002F\u002F or in a constructor ANYWHERE in the component tree unmounts the ENTIRE tree.\n\u002F\u002F This is deliberate: React's position is that leaving a corrupted UI on screen\n\u002F\u002F (parts from before the error mixed with a broken subtree) is WORSE than showing\n\u002F\u002F nothing, since a partially-broken UI can mislead a user.\n\nfunction ProductPrice({ price }) {\n  return \u003Cspan>${price.toFixed(2)}\u003C\u002Fspan>\n  \u002F\u002F If price is undefined (malformed API response), .toFixed throws — and with\n  \u002F\u002F no error boundary above it, the WHOLE app unmounts to a blank screen.\n}\n\n\u002F\u002F A single malformed price field deep in a product list, taking down an entire\n\u002F\u002F e-commerce page, is precisely the failure mode error boundaries exist to contain.\n",[30,130,131,136,141,146,151,156,160,181,213,218,223,228,232,237],{"__ignoreMap":28},[33,132,133],{"class":35,"line":36},[33,134,135],{"class":39},"\u002F\u002F By default, an uncaught error thrown during rendering, in a lifecycle method,\n",[33,137,138],{"class":35,"line":43},[33,139,140],{"class":39},"\u002F\u002F or in a constructor ANYWHERE in the component tree unmounts the ENTIRE tree.\n",[33,142,143],{"class":35,"line":49},[33,144,145],{"class":39},"\u002F\u002F This is deliberate: React's position is that leaving a corrupted UI on screen\n",[33,147,148],{"class":35,"line":55},[33,149,150],{"class":39},"\u002F\u002F (parts from before the error mixed with a broken subtree) is WORSE than showing\n",[33,152,153],{"class":35,"line":61},[33,154,155],{"class":39},"\u002F\u002F nothing, since a partially-broken UI can mislead a user.\n",[33,157,158],{"class":35,"line":68},[33,159,65],{"emptyLinePlaceholder":64},[33,161,162,166,170,174,178],{"class":35,"line":74},[33,163,165],{"class":164},"svdQ7","function",[33,167,169],{"class":168},"sIsaT"," ProductPrice",[33,171,173],{"class":172},"ssxIu","({ ",[33,175,177],{"class":176},"sCrzJ","price",[33,179,180],{"class":172}," }) {\n",[33,182,183,186,189,192,195,198,201,205,208,210],{"class":35,"line":80},[33,184,185],{"class":164},"  return",[33,187,188],{"class":172}," \u003C",[33,190,33],{"class":191},"sk71V",[33,193,194],{"class":172},">${price.",[33,196,197],{"class":168},"toFixed",[33,199,200],{"class":172},"(",[33,202,204],{"class":203},"snvgF","2",[33,206,207],{"class":172},")}\u003C\u002F",[33,209,33],{"class":191},[33,211,212],{"class":172},">\n",[33,214,215],{"class":35,"line":86},[33,216,217],{"class":39},"  \u002F\u002F If price is undefined (malformed API response), .toFixed throws — and with\n",[33,219,220],{"class":35,"line":92},[33,221,222],{"class":39},"  \u002F\u002F no error boundary above it, the WHOLE app unmounts to a blank screen.\n",[33,224,225],{"class":35,"line":98},[33,226,227],{"class":172},"}\n",[33,229,230],{"class":35,"line":103},[33,231,65],{"emptyLinePlaceholder":64},[33,233,234],{"class":35,"line":109},[33,235,236],{"class":39},"\u002F\u002F A single malformed price field deep in a product list, taking down an entire\n",[33,238,239],{"class":35,"line":115},[33,240,241],{"class":39},"\u002F\u002F e-commerce page, is precisely the failure mode error boundaries exist to contain.\n",[14,243,245],{"id":244},"defining-a-basic-error-boundary","Defining a Basic Error Boundary",[19,247,249],{"filename":248,"language":22},"basic_boundary.js",[24,250,252],{"className":26,"code":251,"language":22,"meta":28,"style":28},"import { Component } from 'react'\n\nclass ErrorBoundary extends Component {\n  constructor(props) {\n    super(props)\n    this.state = { hasError: false }\n  }\n\n  static getDerivedStateFromError(error) {\n    \u002F\u002F Called during the RENDER phase — must be PURE, no side effects.\n    \u002F\u002F React may call it multiple times or discard the result under certain\n    \u002F\u002F circumstances (e.g. Strict Mode double-invocation in dev).\n    \u002F\u002F Its ONLY job: return a new state object that triggers the fallback render.\n    return { hasError: true }\n  }\n\n  componentDidCatch(error, errorInfo) {\n    \u002F\u002F Called during the COMMIT phase (after DOM updates) — side effects go here.\n    \u002F\u002F This is where you log to Sentry, Datadog, an internal error service, etc.\n    \u002F\u002F errorInfo.componentStack is a string showing which component tree the error\n    \u002F\u002F propagated through — invaluable for debugging which INSTANCE failed.\n    logErrorToService(error, errorInfo.componentStack)\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return this.props.fallback\n    }\n    return this.props.children\n  }\n}\n\n\u002F\u002F Usage:\nfunction App() {\n  return (\n    \u003CErrorBoundary fallback={\u003Cp>Something went wrong. Please refresh.\u003C\u002Fp>}>\n      \u003CProductList \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n}\n",[30,253,254,269,273,290,303,311,331,336,340,355,360,365,370,375,387,392,397,415,421,427,433,439,448,453,458,467,482,494,500,510,515,520,525,531,541,549,577,589,599,605],{"__ignoreMap":28},[33,255,256,259,262,265],{"class":35,"line":36},[33,257,258],{"class":164},"import",[33,260,261],{"class":172}," { Component } ",[33,263,264],{"class":164},"from",[33,266,268],{"class":267},"sJ6F3"," 'react'\n",[33,270,271],{"class":35,"line":43},[33,272,65],{"emptyLinePlaceholder":64},[33,274,275,278,281,284,287],{"class":35,"line":49},[33,276,277],{"class":164},"class",[33,279,280],{"class":168}," ErrorBoundary",[33,282,283],{"class":164}," extends",[33,285,286],{"class":168}," Component",[33,288,289],{"class":172}," {\n",[33,291,292,295,297,300],{"class":35,"line":55},[33,293,294],{"class":164},"  constructor",[33,296,200],{"class":172},[33,298,299],{"class":176},"props",[33,301,302],{"class":172},") {\n",[33,304,305,308],{"class":35,"line":61},[33,306,307],{"class":203},"    super",[33,309,310],{"class":172},"(props)\n",[33,312,313,316,319,322,325,328],{"class":35,"line":68},[33,314,315],{"class":203},"    this",[33,317,318],{"class":172},".state ",[33,320,321],{"class":164},"=",[33,323,324],{"class":172}," { hasError: ",[33,326,327],{"class":203},"false",[33,329,330],{"class":172}," }\n",[33,332,333],{"class":35,"line":74},[33,334,335],{"class":172},"  }\n",[33,337,338],{"class":35,"line":80},[33,339,65],{"emptyLinePlaceholder":64},[33,341,342,345,348,350,353],{"class":35,"line":86},[33,343,344],{"class":164},"  static",[33,346,347],{"class":168}," getDerivedStateFromError",[33,349,200],{"class":172},[33,351,352],{"class":176},"error",[33,354,302],{"class":172},[33,356,357],{"class":35,"line":92},[33,358,359],{"class":39},"    \u002F\u002F Called during the RENDER phase — must be PURE, no side effects.\n",[33,361,362],{"class":35,"line":98},[33,363,364],{"class":39},"    \u002F\u002F React may call it multiple times or discard the result under certain\n",[33,366,367],{"class":35,"line":103},[33,368,369],{"class":39},"    \u002F\u002F circumstances (e.g. Strict Mode double-invocation in dev).\n",[33,371,372],{"class":35,"line":109},[33,373,374],{"class":39},"    \u002F\u002F Its ONLY job: return a new state object that triggers the fallback render.\n",[33,376,377,380,382,385],{"class":35,"line":115},[33,378,379],{"class":164},"    return",[33,381,324],{"class":172},[33,383,384],{"class":203},"true",[33,386,330],{"class":172},[33,388,390],{"class":35,"line":389},15,[33,391,335],{"class":172},[33,393,395],{"class":35,"line":394},16,[33,396,65],{"emptyLinePlaceholder":64},[33,398,400,403,405,407,410,413],{"class":35,"line":399},17,[33,401,402],{"class":168},"  componentDidCatch",[33,404,200],{"class":172},[33,406,352],{"class":176},[33,408,409],{"class":172},", ",[33,411,412],{"class":176},"errorInfo",[33,414,302],{"class":172},[33,416,418],{"class":35,"line":417},18,[33,419,420],{"class":39},"    \u002F\u002F Called during the COMMIT phase (after DOM updates) — side effects go here.\n",[33,422,424],{"class":35,"line":423},19,[33,425,426],{"class":39},"    \u002F\u002F This is where you log to Sentry, Datadog, an internal error service, etc.\n",[33,428,430],{"class":35,"line":429},20,[33,431,432],{"class":39},"    \u002F\u002F errorInfo.componentStack is a string showing which component tree the error\n",[33,434,436],{"class":35,"line":435},21,[33,437,438],{"class":39},"    \u002F\u002F propagated through — invaluable for debugging which INSTANCE failed.\n",[33,440,442,445],{"class":35,"line":441},22,[33,443,444],{"class":168},"    logErrorToService",[33,446,447],{"class":172},"(error, errorInfo.componentStack)\n",[33,449,451],{"class":35,"line":450},23,[33,452,335],{"class":172},[33,454,456],{"class":35,"line":455},24,[33,457,65],{"emptyLinePlaceholder":64},[33,459,461,464],{"class":35,"line":460},25,[33,462,463],{"class":168},"  render",[33,465,466],{"class":172},"() {\n",[33,468,470,473,476,479],{"class":35,"line":469},26,[33,471,472],{"class":164},"    if",[33,474,475],{"class":172}," (",[33,477,478],{"class":203},"this",[33,480,481],{"class":172},".state.hasError) {\n",[33,483,485,488,491],{"class":35,"line":484},27,[33,486,487],{"class":164},"      return",[33,489,490],{"class":203}," this",[33,492,493],{"class":172},".props.fallback\n",[33,495,497],{"class":35,"line":496},28,[33,498,499],{"class":172},"    }\n",[33,501,503,505,507],{"class":35,"line":502},29,[33,504,379],{"class":164},[33,506,490],{"class":203},[33,508,509],{"class":172},".props.children\n",[33,511,513],{"class":35,"line":512},30,[33,514,335],{"class":172},[33,516,518],{"class":35,"line":517},31,[33,519,227],{"class":172},[33,521,523],{"class":35,"line":522},32,[33,524,65],{"emptyLinePlaceholder":64},[33,526,528],{"class":35,"line":527},33,[33,529,530],{"class":39},"\u002F\u002F Usage:\n",[33,532,534,536,539],{"class":35,"line":533},34,[33,535,165],{"class":164},[33,537,538],{"class":168}," App",[33,540,466],{"class":172},[33,542,544,546],{"class":35,"line":543},35,[33,545,185],{"class":164},[33,547,548],{"class":172}," (\n",[33,550,552,555,558,561,563,566,569,572,574],{"class":35,"line":551},36,[33,553,554],{"class":172},"    \u003C",[33,556,557],{"class":203},"ErrorBoundary",[33,559,560],{"class":168}," fallback",[33,562,321],{"class":164},[33,564,565],{"class":172},"{\u003C",[33,567,568],{"class":191},"p",[33,570,571],{"class":172},">Something went wrong. Please refresh.\u003C\u002F",[33,573,568],{"class":191},[33,575,576],{"class":172},">}>\n",[33,578,580,583,586],{"class":35,"line":579},37,[33,581,582],{"class":172},"      \u003C",[33,584,585],{"class":203},"ProductList",[33,587,588],{"class":172}," \u002F>\n",[33,590,592,595,597],{"class":35,"line":591},38,[33,593,594],{"class":172},"    \u003C\u002F",[33,596,557],{"class":203},[33,598,212],{"class":172},[33,600,602],{"class":35,"line":601},39,[33,603,604],{"class":172},"  )\n",[33,606,608],{"class":35,"line":607},40,[33,609,227],{"class":172},[611,612,614,617,618],"h3",{"id":613},"getderivedstatefromerror-vs-componentdidcatch",[30,615,616],{},"getDerivedStateFromError"," vs ",[30,619,620],{},"componentDidCatch",[19,622,624],{"filename":623,"language":22},"two_methods.js",[24,625,627],{"className":26,"code":626,"language":22,"meta":28,"style":28},"\u002F\u002F The split mirrors React's render\u002Fcommit phase separation (chapter 6):\n\n\u002F\u002F getDerivedStateFromError(error)\n\u002F\u002F   WHEN: during the render phase, right after a child throws\n\u002F\u002F   PURE: no side effects allowed (no logging, no network calls)\n\u002F\u002F   RETURNS: a state object that triggers fallback UI on next render\n\u002F\u002F   CAN'T: access `this` (it's static), access this.props, do anything async\n\n\u002F\u002F componentDidCatch(error, errorInfo)\n\u002F\u002F   WHEN: during the commit phase, after the DOM has been updated with fallback\n\u002F\u002F   IMPURE: side effects allowed and expected (logging, analytics)\n\u002F\u002F   RETURNS: nothing\n\u002F\u002F   CAN: access this.props, this.state, call external services\n\n\u002F\u002F ANTI-PATTERN: logging inside getDerivedStateFromError\nstatic getDerivedStateFromError(error) {\n  logErrorToService(error)  \u002F\u002F WRONG — this is a render-phase method, must be pure\n  return { hasError: true }\n}\n\u002F\u002F React may call this multiple times in dev (Strict Mode) or discard the render\n\u002F\u002F result entirely — your logging service would receive duplicate or spurious errors.\n\n\u002F\u002F CORRECT: log in componentDidCatch only\ncomponentDidCatch(error, errorInfo) {\n  logErrorToService(error, errorInfo.componentStack)  \u002F\u002F commit phase — safe\n}\n",[30,628,629,634,638,643,648,653,658,663,667,672,677,682,687,692,696,701,711,722,732,736,741,746,750,755,762,772],{"__ignoreMap":28},[33,630,631],{"class":35,"line":36},[33,632,633],{"class":39},"\u002F\u002F The split mirrors React's render\u002Fcommit phase separation (chapter 6):\n",[33,635,636],{"class":35,"line":43},[33,637,65],{"emptyLinePlaceholder":64},[33,639,640],{"class":35,"line":49},[33,641,642],{"class":39},"\u002F\u002F getDerivedStateFromError(error)\n",[33,644,645],{"class":35,"line":55},[33,646,647],{"class":39},"\u002F\u002F   WHEN: during the render phase, right after a child throws\n",[33,649,650],{"class":35,"line":61},[33,651,652],{"class":39},"\u002F\u002F   PURE: no side effects allowed (no logging, no network calls)\n",[33,654,655],{"class":35,"line":68},[33,656,657],{"class":39},"\u002F\u002F   RETURNS: a state object that triggers fallback UI on next render\n",[33,659,660],{"class":35,"line":74},[33,661,662],{"class":39},"\u002F\u002F   CAN'T: access `this` (it's static), access this.props, do anything async\n",[33,664,665],{"class":35,"line":80},[33,666,65],{"emptyLinePlaceholder":64},[33,668,669],{"class":35,"line":86},[33,670,671],{"class":39},"\u002F\u002F componentDidCatch(error, errorInfo)\n",[33,673,674],{"class":35,"line":92},[33,675,676],{"class":39},"\u002F\u002F   WHEN: during the commit phase, after the DOM has been updated with fallback\n",[33,678,679],{"class":35,"line":98},[33,680,681],{"class":39},"\u002F\u002F   IMPURE: side effects allowed and expected (logging, analytics)\n",[33,683,684],{"class":35,"line":103},[33,685,686],{"class":39},"\u002F\u002F   RETURNS: nothing\n",[33,688,689],{"class":35,"line":109},[33,690,691],{"class":39},"\u002F\u002F   CAN: access this.props, this.state, call external services\n",[33,693,694],{"class":35,"line":115},[33,695,65],{"emptyLinePlaceholder":64},[33,697,698],{"class":35,"line":389},[33,699,700],{"class":39},"\u002F\u002F ANTI-PATTERN: logging inside getDerivedStateFromError\n",[33,702,703,706,708],{"class":35,"line":394},[33,704,705],{"class":172},"static ",[33,707,616],{"class":168},[33,709,710],{"class":172},"(error) {\n",[33,712,713,716,719],{"class":35,"line":399},[33,714,715],{"class":168},"  logErrorToService",[33,717,718],{"class":172},"(error)  ",[33,720,721],{"class":39},"\u002F\u002F WRONG — this is a render-phase method, must be pure\n",[33,723,724,726,728,730],{"class":35,"line":417},[33,725,185],{"class":164},[33,727,324],{"class":172},[33,729,384],{"class":203},[33,731,330],{"class":172},[33,733,734],{"class":35,"line":423},[33,735,227],{"class":172},[33,737,738],{"class":35,"line":429},[33,739,740],{"class":39},"\u002F\u002F React may call this multiple times in dev (Strict Mode) or discard the render\n",[33,742,743],{"class":35,"line":435},[33,744,745],{"class":39},"\u002F\u002F result entirely — your logging service would receive duplicate or spurious errors.\n",[33,747,748],{"class":35,"line":441},[33,749,65],{"emptyLinePlaceholder":64},[33,751,752],{"class":35,"line":450},[33,753,754],{"class":39},"\u002F\u002F CORRECT: log in componentDidCatch only\n",[33,756,757,759],{"class":35,"line":455},[33,758,620],{"class":168},[33,760,761],{"class":172},"(error, errorInfo) {\n",[33,763,764,766,769],{"class":35,"line":460},[33,765,715],{"class":168},[33,767,768],{"class":172},"(error, errorInfo.componentStack)  ",[33,770,771],{"class":39},"\u002F\u002F commit phase — safe\n",[33,773,774],{"class":35,"line":469},[33,775,227],{"class":172},[14,777,779],{"id":778},"what-error-boundaries-catch-and-what-they-dont","What Error Boundaries Catch — and What They Don't",[19,781,783],{"filename":782,"language":22},"catch_scope.js",[24,784,786],{"className":26,"code":785,"language":22,"meta":28,"style":28},"\u002F\u002F THE single most misunderstood fact about error boundaries:\n\u002F\u002F They catch errors thrown during RENDERING, in LIFECYCLE METHODS, and in\n\u002F\u002F CONSTRUCTORS of the tree below them — and NOTHING ELSE.\n\n\u002F\u002F NOT caught by error boundaries:\n\u002F\u002F 1. Event handlers (onClick, onChange) — run outside React's render cycle\n\u002F\u002F 2. Asynchronous code (setTimeout, promises, async\u002Fawait)\n\u002F\u002F 3. Server-side rendering errors — boundaries only run on the client\n\u002F\u002F 4. Errors in the error boundary's OWN render method — need a higher boundary\n\nfunction CheckoutButton() {\n  function handleClick() {\n    throw new Error('Payment failed')\n    \u002F\u002F NOT caught by any error boundary — this is an event handler.\n    \u002F\u002F It becomes an uncaught runtime error (visible in console) but does NOT\n    \u002F\u002F unmount the tree. The component stays rendered and interactive.\n  }\n  return \u003Cbutton onClick={handleClick}>Pay Now\u003C\u002Fbutton>\n}\n\n\u002F\u002F Async errors — NOT caught:\nfunction UserAvatar({ userId }) {\n  useEffect(() => {\n    fetchUser(userId).catch(err => {\n      \u002F\u002F Must be handled HERE, explicitly. An error boundary above this component\n      \u002F\u002F will NEVER see this rejection — it happens asynchronously after render\n      \u002F\u002F committed, long after the render phase ended.\n      console.error(err)\n      reportError(err)\n    })\n  }, [userId])\n  return \u003Cimg src={`\u002Favatars\u002F${userId}.png`} \u002F>\n}\n",[30,787,788,793,798,803,807,812,817,822,827,832,836,845,855,874,879,884,889,893,914,918,922,927,941,954,975,980,985,990,1000,1007,1012,1017,1045],{"__ignoreMap":28},[33,789,790],{"class":35,"line":36},[33,791,792],{"class":39},"\u002F\u002F THE single most misunderstood fact about error boundaries:\n",[33,794,795],{"class":35,"line":43},[33,796,797],{"class":39},"\u002F\u002F They catch errors thrown during RENDERING, in LIFECYCLE METHODS, and in\n",[33,799,800],{"class":35,"line":49},[33,801,802],{"class":39},"\u002F\u002F CONSTRUCTORS of the tree below them — and NOTHING ELSE.\n",[33,804,805],{"class":35,"line":55},[33,806,65],{"emptyLinePlaceholder":64},[33,808,809],{"class":35,"line":61},[33,810,811],{"class":39},"\u002F\u002F NOT caught by error boundaries:\n",[33,813,814],{"class":35,"line":68},[33,815,816],{"class":39},"\u002F\u002F 1. Event handlers (onClick, onChange) — run outside React's render cycle\n",[33,818,819],{"class":35,"line":74},[33,820,821],{"class":39},"\u002F\u002F 2. Asynchronous code (setTimeout, promises, async\u002Fawait)\n",[33,823,824],{"class":35,"line":80},[33,825,826],{"class":39},"\u002F\u002F 3. Server-side rendering errors — boundaries only run on the client\n",[33,828,829],{"class":35,"line":86},[33,830,831],{"class":39},"\u002F\u002F 4. Errors in the error boundary's OWN render method — need a higher boundary\n",[33,833,834],{"class":35,"line":92},[33,835,65],{"emptyLinePlaceholder":64},[33,837,838,840,843],{"class":35,"line":98},[33,839,165],{"class":164},[33,841,842],{"class":168}," CheckoutButton",[33,844,466],{"class":172},[33,846,847,850,853],{"class":35,"line":103},[33,848,849],{"class":164},"  function",[33,851,852],{"class":168}," handleClick",[33,854,466],{"class":172},[33,856,857,860,863,866,868,871],{"class":35,"line":109},[33,858,859],{"class":164},"    throw",[33,861,862],{"class":164}," new",[33,864,865],{"class":168}," Error",[33,867,200],{"class":172},[33,869,870],{"class":267},"'Payment failed'",[33,872,873],{"class":172},")\n",[33,875,876],{"class":35,"line":115},[33,877,878],{"class":39},"    \u002F\u002F NOT caught by any error boundary — this is an event handler.\n",[33,880,881],{"class":35,"line":389},[33,882,883],{"class":39},"    \u002F\u002F It becomes an uncaught runtime error (visible in console) but does NOT\n",[33,885,886],{"class":35,"line":394},[33,887,888],{"class":39},"    \u002F\u002F unmount the tree. The component stays rendered and interactive.\n",[33,890,891],{"class":35,"line":399},[33,892,335],{"class":172},[33,894,895,897,899,902,905,907,910,912],{"class":35,"line":417},[33,896,185],{"class":164},[33,898,188],{"class":172},[33,900,901],{"class":191},"button",[33,903,904],{"class":168}," onClick",[33,906,321],{"class":164},[33,908,909],{"class":172},"{handleClick}>Pay Now\u003C\u002F",[33,911,901],{"class":191},[33,913,212],{"class":172},[33,915,916],{"class":35,"line":423},[33,917,227],{"class":172},[33,919,920],{"class":35,"line":429},[33,921,65],{"emptyLinePlaceholder":64},[33,923,924],{"class":35,"line":435},[33,925,926],{"class":39},"\u002F\u002F Async errors — NOT caught:\n",[33,928,929,931,934,936,939],{"class":35,"line":441},[33,930,165],{"class":164},[33,932,933],{"class":168}," UserAvatar",[33,935,173],{"class":172},[33,937,938],{"class":176},"userId",[33,940,180],{"class":172},[33,942,943,946,949,952],{"class":35,"line":450},[33,944,945],{"class":168},"  useEffect",[33,947,948],{"class":172},"(() ",[33,950,951],{"class":164},"=>",[33,953,289],{"class":172},[33,955,956,959,962,965,967,970,973],{"class":35,"line":455},[33,957,958],{"class":168},"    fetchUser",[33,960,961],{"class":172},"(userId).",[33,963,964],{"class":168},"catch",[33,966,200],{"class":172},[33,968,969],{"class":176},"err",[33,971,972],{"class":164}," =>",[33,974,289],{"class":172},[33,976,977],{"class":35,"line":460},[33,978,979],{"class":39},"      \u002F\u002F Must be handled HERE, explicitly. An error boundary above this component\n",[33,981,982],{"class":35,"line":469},[33,983,984],{"class":39},"      \u002F\u002F will NEVER see this rejection — it happens asynchronously after render\n",[33,986,987],{"class":35,"line":484},[33,988,989],{"class":39},"      \u002F\u002F committed, long after the render phase ended.\n",[33,991,992,995,997],{"class":35,"line":496},[33,993,994],{"class":172},"      console.",[33,996,352],{"class":168},[33,998,999],{"class":172},"(err)\n",[33,1001,1002,1005],{"class":35,"line":502},[33,1003,1004],{"class":168},"      reportError",[33,1006,999],{"class":172},[33,1008,1009],{"class":35,"line":512},[33,1010,1011],{"class":172},"    })\n",[33,1013,1014],{"class":35,"line":517},[33,1015,1016],{"class":172},"  }, [userId])\n",[33,1018,1019,1021,1023,1026,1029,1031,1034,1037,1039,1042],{"class":35,"line":522},[33,1020,185],{"class":164},[33,1022,188],{"class":172},[33,1024,1025],{"class":191},"img",[33,1027,1028],{"class":168}," src",[33,1030,321],{"class":164},[33,1032,1033],{"class":172},"{",[33,1035,1036],{"class":267},"`\u002Favatars\u002F${",[33,1038,938],{"class":172},[33,1040,1041],{"class":267},"}.png`",[33,1043,1044],{"class":172},"} \u002F>\n",[33,1046,1047],{"class":35,"line":527},[33,1048,227],{"class":172},[611,1050,1052],{"id":1051},"handling-event-handler-errors","Handling Event Handler Errors",[19,1054,1056],{"filename":1055,"language":22},"event_handler_errors.js",[24,1057,1059],{"className":26,"code":1058,"language":22,"meta":28,"style":28},"\u002F\u002F For event handlers, use a plain try\u002Fcatch + local state to drive fallback UI.\n\u002F\u002F This is the ONLY mechanism — error boundaries cannot intercept event handlers.\n\nfunction CheckoutButton() {\n  const [error, setError] = useState(null)\n\n  async function handleClick() {\n    try {\n      await processPayment()\n    } catch (err) {\n      setError(err)  \u002F\u002F local state drives a re-render with error UI\n    }\n  }\n\n  if (error) return \u003CPaymentErrorMessage error={error} onRetry={() => setError(null)} \u002F>\n  return \u003Cbutton onClick={handleClick}>Pay Now\u003C\u002Fbutton>\n}\n\n\u002F\u002F For async errors in effects, handle them in the effect's .catch():\nfunction useFetchData(url) {\n  const [state, setState] = useState({ status: 'idle', data: null, error: null })\n\n  useEffect(() => {\n    if (!url) return\n    const controller = new AbortController()\n    setState({ status: 'loading', data: null, error: null })\n    fetch(url, { signal: controller.signal })\n      .then(res => {\n        if (!res.ok) throw new Error(`HTTP ${res.status}`)\n        return res.json()\n      })\n      .then(data => setState({ status: 'success', data, error: null }))\n      .catch(err => {\n        if (err.name !== 'AbortError') {\n          setState({ status: 'error', data: null, error: err })\n        }\n      })\n    return () => controller.abort()\n  }, [url])\n\n  return state\n}\n",[30,1060,1061,1066,1071,1075,1083,1113,1117,1129,1136,1147,1157,1168,1172,1176,1180,1224,1242,1246,1250,1255,1269,1308,1312,1322,1337,1355,1375,1383,1400,1437,1450,1455,1484,1498,1513,1530,1535,1539,1556,1561,1565,1573],{"__ignoreMap":28},[33,1062,1063],{"class":35,"line":36},[33,1064,1065],{"class":39},"\u002F\u002F For event handlers, use a plain try\u002Fcatch + local state to drive fallback UI.\n",[33,1067,1068],{"class":35,"line":43},[33,1069,1070],{"class":39},"\u002F\u002F This is the ONLY mechanism — error boundaries cannot intercept event handlers.\n",[33,1072,1073],{"class":35,"line":49},[33,1074,65],{"emptyLinePlaceholder":64},[33,1076,1077,1079,1081],{"class":35,"line":55},[33,1078,165],{"class":164},[33,1080,842],{"class":168},[33,1082,466],{"class":172},[33,1084,1085,1088,1091,1093,1095,1098,1101,1103,1106,1108,1111],{"class":35,"line":61},[33,1086,1087],{"class":164},"  const",[33,1089,1090],{"class":172}," [",[33,1092,352],{"class":203},[33,1094,409],{"class":172},[33,1096,1097],{"class":203},"setError",[33,1099,1100],{"class":172},"] ",[33,1102,321],{"class":164},[33,1104,1105],{"class":168}," useState",[33,1107,200],{"class":172},[33,1109,1110],{"class":203},"null",[33,1112,873],{"class":172},[33,1114,1115],{"class":35,"line":68},[33,1116,65],{"emptyLinePlaceholder":64},[33,1118,1119,1122,1125,1127],{"class":35,"line":74},[33,1120,1121],{"class":164},"  async",[33,1123,1124],{"class":164}," function",[33,1126,852],{"class":168},[33,1128,466],{"class":172},[33,1130,1131,1134],{"class":35,"line":80},[33,1132,1133],{"class":164},"    try",[33,1135,289],{"class":172},[33,1137,1138,1141,1144],{"class":35,"line":86},[33,1139,1140],{"class":164},"      await",[33,1142,1143],{"class":168}," processPayment",[33,1145,1146],{"class":172},"()\n",[33,1148,1149,1152,1154],{"class":35,"line":92},[33,1150,1151],{"class":172},"    } ",[33,1153,964],{"class":164},[33,1155,1156],{"class":172}," (err) {\n",[33,1158,1159,1162,1165],{"class":35,"line":98},[33,1160,1161],{"class":168},"      setError",[33,1163,1164],{"class":172},"(err)  ",[33,1166,1167],{"class":39},"\u002F\u002F local state drives a re-render with error UI\n",[33,1169,1170],{"class":35,"line":103},[33,1171,499],{"class":172},[33,1173,1174],{"class":35,"line":109},[33,1175,335],{"class":172},[33,1177,1178],{"class":35,"line":115},[33,1179,65],{"emptyLinePlaceholder":64},[33,1181,1182,1185,1188,1191,1193,1196,1199,1201,1204,1207,1209,1212,1214,1217,1219,1221],{"class":35,"line":389},[33,1183,1184],{"class":164},"  if",[33,1186,1187],{"class":172}," (error) ",[33,1189,1190],{"class":164},"return",[33,1192,188],{"class":172},[33,1194,1195],{"class":203},"PaymentErrorMessage",[33,1197,1198],{"class":168}," error",[33,1200,321],{"class":164},[33,1202,1203],{"class":172},"{error} ",[33,1205,1206],{"class":168},"onRetry",[33,1208,321],{"class":164},[33,1210,1211],{"class":172},"{() ",[33,1213,951],{"class":164},[33,1215,1216],{"class":168}," setError",[33,1218,200],{"class":172},[33,1220,1110],{"class":203},[33,1222,1223],{"class":172},")} \u002F>\n",[33,1225,1226,1228,1230,1232,1234,1236,1238,1240],{"class":35,"line":394},[33,1227,185],{"class":164},[33,1229,188],{"class":172},[33,1231,901],{"class":191},[33,1233,904],{"class":168},[33,1235,321],{"class":164},[33,1237,909],{"class":172},[33,1239,901],{"class":191},[33,1241,212],{"class":172},[33,1243,1244],{"class":35,"line":399},[33,1245,227],{"class":172},[33,1247,1248],{"class":35,"line":417},[33,1249,65],{"emptyLinePlaceholder":64},[33,1251,1252],{"class":35,"line":423},[33,1253,1254],{"class":39},"\u002F\u002F For async errors in effects, handle them in the effect's .catch():\n",[33,1256,1257,1259,1262,1264,1267],{"class":35,"line":429},[33,1258,165],{"class":164},[33,1260,1261],{"class":168}," useFetchData",[33,1263,200],{"class":172},[33,1265,1266],{"class":176},"url",[33,1268,302],{"class":172},[33,1270,1271,1273,1275,1278,1280,1283,1285,1287,1289,1292,1295,1298,1300,1303,1305],{"class":35,"line":435},[33,1272,1087],{"class":164},[33,1274,1090],{"class":172},[33,1276,1277],{"class":203},"state",[33,1279,409],{"class":172},[33,1281,1282],{"class":203},"setState",[33,1284,1100],{"class":172},[33,1286,321],{"class":164},[33,1288,1105],{"class":168},[33,1290,1291],{"class":172},"({ status: ",[33,1293,1294],{"class":267},"'idle'",[33,1296,1297],{"class":172},", data: ",[33,1299,1110],{"class":203},[33,1301,1302],{"class":172},", error: ",[33,1304,1110],{"class":203},[33,1306,1307],{"class":172}," })\n",[33,1309,1310],{"class":35,"line":441},[33,1311,65],{"emptyLinePlaceholder":64},[33,1313,1314,1316,1318,1320],{"class":35,"line":450},[33,1315,945],{"class":168},[33,1317,948],{"class":172},[33,1319,951],{"class":164},[33,1321,289],{"class":172},[33,1323,1324,1326,1328,1331,1334],{"class":35,"line":455},[33,1325,472],{"class":164},[33,1327,475],{"class":172},[33,1329,1330],{"class":164},"!",[33,1332,1333],{"class":172},"url) ",[33,1335,1336],{"class":164},"return\n",[33,1338,1339,1342,1345,1348,1350,1353],{"class":35,"line":460},[33,1340,1341],{"class":164},"    const",[33,1343,1344],{"class":203}," controller",[33,1346,1347],{"class":164}," =",[33,1349,862],{"class":164},[33,1351,1352],{"class":168}," AbortController",[33,1354,1146],{"class":172},[33,1356,1357,1360,1362,1365,1367,1369,1371,1373],{"class":35,"line":469},[33,1358,1359],{"class":168},"    setState",[33,1361,1291],{"class":172},[33,1363,1364],{"class":267},"'loading'",[33,1366,1297],{"class":172},[33,1368,1110],{"class":203},[33,1370,1302],{"class":172},[33,1372,1110],{"class":203},[33,1374,1307],{"class":172},[33,1376,1377,1380],{"class":35,"line":484},[33,1378,1379],{"class":168},"    fetch",[33,1381,1382],{"class":172},"(url, { signal: controller.signal })\n",[33,1384,1385,1388,1391,1393,1396,1398],{"class":35,"line":496},[33,1386,1387],{"class":172},"      .",[33,1389,1390],{"class":168},"then",[33,1392,200],{"class":172},[33,1394,1395],{"class":176},"res",[33,1397,972],{"class":164},[33,1399,289],{"class":172},[33,1401,1402,1405,1407,1409,1412,1415,1417,1419,1421,1424,1426,1429,1432,1435],{"class":35,"line":502},[33,1403,1404],{"class":164},"        if",[33,1406,475],{"class":172},[33,1408,1330],{"class":164},[33,1410,1411],{"class":172},"res.ok) ",[33,1413,1414],{"class":164},"throw",[33,1416,862],{"class":164},[33,1418,865],{"class":168},[33,1420,200],{"class":172},[33,1422,1423],{"class":267},"`HTTP ${",[33,1425,1395],{"class":172},[33,1427,1428],{"class":267},".",[33,1430,1431],{"class":172},"status",[33,1433,1434],{"class":267},"}`",[33,1436,873],{"class":172},[33,1438,1439,1442,1445,1448],{"class":35,"line":512},[33,1440,1441],{"class":164},"        return",[33,1443,1444],{"class":172}," res.",[33,1446,1447],{"class":168},"json",[33,1449,1146],{"class":172},[33,1451,1452],{"class":35,"line":517},[33,1453,1454],{"class":172},"      })\n",[33,1456,1457,1459,1461,1463,1466,1468,1471,1473,1476,1479,1481],{"class":35,"line":522},[33,1458,1387],{"class":172},[33,1460,1390],{"class":168},[33,1462,200],{"class":172},[33,1464,1465],{"class":176},"data",[33,1467,972],{"class":164},[33,1469,1470],{"class":168}," setState",[33,1472,1291],{"class":172},[33,1474,1475],{"class":267},"'success'",[33,1477,1478],{"class":172},", data, error: ",[33,1480,1110],{"class":203},[33,1482,1483],{"class":172}," }))\n",[33,1485,1486,1488,1490,1492,1494,1496],{"class":35,"line":527},[33,1487,1387],{"class":172},[33,1489,964],{"class":168},[33,1491,200],{"class":172},[33,1493,969],{"class":176},[33,1495,972],{"class":164},[33,1497,289],{"class":172},[33,1499,1500,1502,1505,1508,1511],{"class":35,"line":533},[33,1501,1404],{"class":164},[33,1503,1504],{"class":172}," (err.name ",[33,1506,1507],{"class":164},"!==",[33,1509,1510],{"class":267}," 'AbortError'",[33,1512,302],{"class":172},[33,1514,1515,1518,1520,1523,1525,1527],{"class":35,"line":543},[33,1516,1517],{"class":168},"          setState",[33,1519,1291],{"class":172},[33,1521,1522],{"class":267},"'error'",[33,1524,1297],{"class":172},[33,1526,1110],{"class":203},[33,1528,1529],{"class":172},", error: err })\n",[33,1531,1532],{"class":35,"line":551},[33,1533,1534],{"class":172},"        }\n",[33,1536,1537],{"class":35,"line":579},[33,1538,1454],{"class":172},[33,1540,1541,1543,1546,1548,1551,1554],{"class":35,"line":591},[33,1542,379],{"class":164},[33,1544,1545],{"class":172}," () ",[33,1547,951],{"class":164},[33,1549,1550],{"class":172}," controller.",[33,1552,1553],{"class":168},"abort",[33,1555,1146],{"class":172},[33,1557,1558],{"class":35,"line":601},[33,1559,1560],{"class":172},"  }, [url])\n",[33,1562,1563],{"class":35,"line":607},[33,1564,65],{"emptyLinePlaceholder":64},[33,1566,1568,1570],{"class":35,"line":1567},41,[33,1569,185],{"class":164},[33,1571,1572],{"class":172}," state\n",[33,1574,1576],{"class":35,"line":1575},42,[33,1577,227],{"class":172},[14,1579,1581],{"id":1580},"error-boundary-granularity-nesting-and-blast-radius","Error Boundary Granularity: Nesting and Blast Radius",[19,1583,1585],{"filename":1584,"language":22},"granularity.js",[24,1586,1588],{"className":26,"code":1587,"language":22,"meta":28,"style":28},"\u002F\u002F A single boundary at the app root is better than nothing, but means ANY\n\u002F\u002F rendering error blanks the ENTIRE app — a chat widget's bug taking down an\n\u002F\u002F otherwise-functional dashboard is a worse outcome than necessary.\n\n\u002F\u002F COARSE: one error anywhere blanks the whole app\nfunction CoarseApp() {\n  return (\n    \u003CErrorBoundary fallback={\u003CFullPageError \u002F>}>\n      \u003CHeader \u002F>\n      \u003CSidebar \u002F>\n      \u003CMainContent \u002F>\n      \u003CChatWidget \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n}\n\n\u002F\u002F GRANULAR: an error in one region degrades only that region\nfunction GranularApp() {\n  return (\n    \u003C>\n      \u003CErrorBoundary fallback={\u003CHeaderFallback \u002F>}>\u003CHeader \u002F>\u003C\u002FErrorBoundary>\n      \u003CErrorBoundary fallback={\u003CSidebarFallback \u002F>}>\u003CSidebar \u002F>\u003C\u002FErrorBoundary>\n      \u003CErrorBoundary fallback={\u003CMainContentFallback \u002F>}>\u003CMainContent \u002F>\u003C\u002FErrorBoundary>\n      \u003CErrorBoundary fallback={null}>\u003CChatWidget \u002F>\u003C\u002FErrorBoundary>\n    \u003C\u002F>\n  )\n}\n\n\u002F\u002F RIGHT GRANULARITY: a judgment call weighing blast radius vs. boilerplate.\n\u002F\u002F - Critical, independent regions (nav, main content, widgets) → own boundaries\n\u002F\u002F - Tightly coupled components (a form + its submit button) → share one boundary\n\u002F\u002F - Non-essential widgets → boundary with fallback={null} (silently disappear)\n",[30,1589,1590,1595,1600,1605,1609,1614,1623,1629,1647,1656,1665,1674,1683,1691,1695,1699,1703,1708,1717,1723,1728,1755,1780,1805,1830,1835,1839,1843,1847,1852,1857,1862],{"__ignoreMap":28},[33,1591,1592],{"class":35,"line":36},[33,1593,1594],{"class":39},"\u002F\u002F A single boundary at the app root is better than nothing, but means ANY\n",[33,1596,1597],{"class":35,"line":43},[33,1598,1599],{"class":39},"\u002F\u002F rendering error blanks the ENTIRE app — a chat widget's bug taking down an\n",[33,1601,1602],{"class":35,"line":49},[33,1603,1604],{"class":39},"\u002F\u002F otherwise-functional dashboard is a worse outcome than necessary.\n",[33,1606,1607],{"class":35,"line":55},[33,1608,65],{"emptyLinePlaceholder":64},[33,1610,1611],{"class":35,"line":61},[33,1612,1613],{"class":39},"\u002F\u002F COARSE: one error anywhere blanks the whole app\n",[33,1615,1616,1618,1621],{"class":35,"line":68},[33,1617,165],{"class":164},[33,1619,1620],{"class":168}," CoarseApp",[33,1622,466],{"class":172},[33,1624,1625,1627],{"class":35,"line":74},[33,1626,185],{"class":164},[33,1628,548],{"class":172},[33,1630,1631,1633,1635,1637,1639,1641,1644],{"class":35,"line":80},[33,1632,554],{"class":172},[33,1634,557],{"class":203},[33,1636,560],{"class":168},[33,1638,321],{"class":164},[33,1640,565],{"class":172},[33,1642,1643],{"class":203},"FullPageError",[33,1645,1646],{"class":172}," \u002F>}>\n",[33,1648,1649,1651,1654],{"class":35,"line":86},[33,1650,582],{"class":172},[33,1652,1653],{"class":203},"Header",[33,1655,588],{"class":172},[33,1657,1658,1660,1663],{"class":35,"line":92},[33,1659,582],{"class":172},[33,1661,1662],{"class":203},"Sidebar",[33,1664,588],{"class":172},[33,1666,1667,1669,1672],{"class":35,"line":98},[33,1668,582],{"class":172},[33,1670,1671],{"class":203},"MainContent",[33,1673,588],{"class":172},[33,1675,1676,1678,1681],{"class":35,"line":103},[33,1677,582],{"class":172},[33,1679,1680],{"class":203},"ChatWidget",[33,1682,588],{"class":172},[33,1684,1685,1687,1689],{"class":35,"line":109},[33,1686,594],{"class":172},[33,1688,557],{"class":203},[33,1690,212],{"class":172},[33,1692,1693],{"class":35,"line":115},[33,1694,604],{"class":172},[33,1696,1697],{"class":35,"line":389},[33,1698,227],{"class":172},[33,1700,1701],{"class":35,"line":394},[33,1702,65],{"emptyLinePlaceholder":64},[33,1704,1705],{"class":35,"line":399},[33,1706,1707],{"class":39},"\u002F\u002F GRANULAR: an error in one region degrades only that region\n",[33,1709,1710,1712,1715],{"class":35,"line":417},[33,1711,165],{"class":164},[33,1713,1714],{"class":168}," GranularApp",[33,1716,466],{"class":172},[33,1718,1719,1721],{"class":35,"line":423},[33,1720,185],{"class":164},[33,1722,548],{"class":172},[33,1724,1725],{"class":35,"line":429},[33,1726,1727],{"class":172},"    \u003C>\n",[33,1729,1730,1732,1734,1736,1738,1740,1743,1746,1748,1751,1753],{"class":35,"line":435},[33,1731,582],{"class":172},[33,1733,557],{"class":203},[33,1735,560],{"class":168},[33,1737,321],{"class":164},[33,1739,565],{"class":172},[33,1741,1742],{"class":203},"HeaderFallback",[33,1744,1745],{"class":172}," \u002F>}>\u003C",[33,1747,1653],{"class":203},[33,1749,1750],{"class":172}," \u002F>\u003C\u002F",[33,1752,557],{"class":203},[33,1754,212],{"class":172},[33,1756,1757,1759,1761,1763,1765,1767,1770,1772,1774,1776,1778],{"class":35,"line":441},[33,1758,582],{"class":172},[33,1760,557],{"class":203},[33,1762,560],{"class":168},[33,1764,321],{"class":164},[33,1766,565],{"class":172},[33,1768,1769],{"class":203},"SidebarFallback",[33,1771,1745],{"class":172},[33,1773,1662],{"class":203},[33,1775,1750],{"class":172},[33,1777,557],{"class":203},[33,1779,212],{"class":172},[33,1781,1782,1784,1786,1788,1790,1792,1795,1797,1799,1801,1803],{"class":35,"line":450},[33,1783,582],{"class":172},[33,1785,557],{"class":203},[33,1787,560],{"class":168},[33,1789,321],{"class":164},[33,1791,565],{"class":172},[33,1793,1794],{"class":203},"MainContentFallback",[33,1796,1745],{"class":172},[33,1798,1671],{"class":203},[33,1800,1750],{"class":172},[33,1802,557],{"class":203},[33,1804,212],{"class":172},[33,1806,1807,1809,1811,1813,1815,1817,1819,1822,1824,1826,1828],{"class":35,"line":455},[33,1808,582],{"class":172},[33,1810,557],{"class":203},[33,1812,560],{"class":168},[33,1814,321],{"class":164},[33,1816,1033],{"class":172},[33,1818,1110],{"class":203},[33,1820,1821],{"class":172},"}>\u003C",[33,1823,1680],{"class":203},[33,1825,1750],{"class":172},[33,1827,557],{"class":203},[33,1829,212],{"class":172},[33,1831,1832],{"class":35,"line":460},[33,1833,1834],{"class":172},"    \u003C\u002F>\n",[33,1836,1837],{"class":35,"line":469},[33,1838,604],{"class":172},[33,1840,1841],{"class":35,"line":484},[33,1842,227],{"class":172},[33,1844,1845],{"class":35,"line":496},[33,1846,65],{"emptyLinePlaceholder":64},[33,1848,1849],{"class":35,"line":502},[33,1850,1851],{"class":39},"\u002F\u002F RIGHT GRANULARITY: a judgment call weighing blast radius vs. boilerplate.\n",[33,1853,1854],{"class":35,"line":512},[33,1855,1856],{"class":39},"\u002F\u002F - Critical, independent regions (nav, main content, widgets) → own boundaries\n",[33,1858,1859],{"class":35,"line":517},[33,1860,1861],{"class":39},"\u002F\u002F - Tightly coupled components (a form + its submit button) → share one boundary\n",[33,1863,1864],{"class":35,"line":522},[33,1865,1866],{"class":39},"\u002F\u002F - Non-essential widgets → boundary with fallback={null} (silently disappear)\n",[611,1868,1870],{"id":1869},"nested-boundaries-and-re-throwing","Nested Boundaries and Re-throwing",[19,1872,1874],{"filename":1873,"language":22},"nested_boundaries.js",[24,1875,1877],{"className":26,"code":1876,"language":22,"meta":28,"style":28},"\u002F\u002F Boundaries can be NESTED. When a child boundary catches an error, it does NOT\n\u002F\u002F propagate to the parent boundary — the child's fallback renders instead.\n\n\u002F\u002F But sometimes you WANT certain errors to bubble up to a higher boundary:\n\u002F\u002F e.g. a deeply-nested component wants to catch \"expected\" errors locally but\n\u002F\u002F let \"unexpected\" (critical) errors propagate to the app-level boundary.\n\nclass SmartBoundary extends Component {\n  state = { hasError: false, error: null }\n\n  static getDerivedStateFromError(error) {\n    return { hasError: true, error }\n  }\n\n  componentDidCatch(error, errorInfo) {\n    const isExpected = error instanceof ValidationError\n    if (!isExpected) {\n      \u002F\u002F Re-throw: this propagates to the PARENT boundary, bypassing this one's\n      \u002F\u002F fallback. componentDidCatch re-throws DO propagate to parent boundaries.\n      throw error\n    }\n    this.props.onError?.(error, errorInfo)\n  }\n\n  render() {\n    if (this.state.hasError && this.state.error instanceof ValidationError) {\n      return this.props.fallback\n    }\n    return this.props.children\n  }\n}\n\n\u002F\u002F App-level boundary catches only the re-thrown (unexpected) errors:\nfunction App() {\n  return (\n    \u003CErrorBoundary fallback={\u003CFullPageError \u002F>}>\n      \u003CSmartBoundary fallback={\u003CFieldError \u002F>}>\n        \u003CForm \u002F>\n      \u003C\u002FSmartBoundary>\n    \u003C\u002FErrorBoundary>\n  )\n}\n",[30,1878,1879,1884,1889,1893,1898,1903,1908,1912,1925,1942,1946,1958,1969,1973,1977,1991,2009,2020,2025,2030,2038,2042,2055,2059,2063,2069,2095,2103,2107,2115,2119,2123,2127,2132,2140,2146,2162,2180,2190,2199,2207,2211],{"__ignoreMap":28},[33,1880,1881],{"class":35,"line":36},[33,1882,1883],{"class":39},"\u002F\u002F Boundaries can be NESTED. When a child boundary catches an error, it does NOT\n",[33,1885,1886],{"class":35,"line":43},[33,1887,1888],{"class":39},"\u002F\u002F propagate to the parent boundary — the child's fallback renders instead.\n",[33,1890,1891],{"class":35,"line":49},[33,1892,65],{"emptyLinePlaceholder":64},[33,1894,1895],{"class":35,"line":55},[33,1896,1897],{"class":39},"\u002F\u002F But sometimes you WANT certain errors to bubble up to a higher boundary:\n",[33,1899,1900],{"class":35,"line":61},[33,1901,1902],{"class":39},"\u002F\u002F e.g. a deeply-nested component wants to catch \"expected\" errors locally but\n",[33,1904,1905],{"class":35,"line":68},[33,1906,1907],{"class":39},"\u002F\u002F let \"unexpected\" (critical) errors propagate to the app-level boundary.\n",[33,1909,1910],{"class":35,"line":74},[33,1911,65],{"emptyLinePlaceholder":64},[33,1913,1914,1916,1919,1921,1923],{"class":35,"line":80},[33,1915,277],{"class":164},[33,1917,1918],{"class":168}," SmartBoundary",[33,1920,283],{"class":164},[33,1922,286],{"class":168},[33,1924,289],{"class":172},[33,1926,1927,1930,1932,1934,1936,1938,1940],{"class":35,"line":86},[33,1928,1929],{"class":176},"  state",[33,1931,1347],{"class":164},[33,1933,324],{"class":172},[33,1935,327],{"class":203},[33,1937,1302],{"class":172},[33,1939,1110],{"class":203},[33,1941,330],{"class":172},[33,1943,1944],{"class":35,"line":92},[33,1945,65],{"emptyLinePlaceholder":64},[33,1947,1948,1950,1952,1954,1956],{"class":35,"line":98},[33,1949,344],{"class":164},[33,1951,347],{"class":168},[33,1953,200],{"class":172},[33,1955,352],{"class":176},[33,1957,302],{"class":172},[33,1959,1960,1962,1964,1966],{"class":35,"line":103},[33,1961,379],{"class":164},[33,1963,324],{"class":172},[33,1965,384],{"class":203},[33,1967,1968],{"class":172},", error }\n",[33,1970,1971],{"class":35,"line":109},[33,1972,335],{"class":172},[33,1974,1975],{"class":35,"line":115},[33,1976,65],{"emptyLinePlaceholder":64},[33,1978,1979,1981,1983,1985,1987,1989],{"class":35,"line":389},[33,1980,402],{"class":168},[33,1982,200],{"class":172},[33,1984,352],{"class":176},[33,1986,409],{"class":172},[33,1988,412],{"class":176},[33,1990,302],{"class":172},[33,1992,1993,1995,1998,2000,2003,2006],{"class":35,"line":394},[33,1994,1341],{"class":164},[33,1996,1997],{"class":203}," isExpected",[33,1999,1347],{"class":164},[33,2001,2002],{"class":172}," error ",[33,2004,2005],{"class":164},"instanceof",[33,2007,2008],{"class":168}," ValidationError\n",[33,2010,2011,2013,2015,2017],{"class":35,"line":399},[33,2012,472],{"class":164},[33,2014,475],{"class":172},[33,2016,1330],{"class":164},[33,2018,2019],{"class":172},"isExpected) {\n",[33,2021,2022],{"class":35,"line":417},[33,2023,2024],{"class":39},"      \u002F\u002F Re-throw: this propagates to the PARENT boundary, bypassing this one's\n",[33,2026,2027],{"class":35,"line":423},[33,2028,2029],{"class":39},"      \u002F\u002F fallback. componentDidCatch re-throws DO propagate to parent boundaries.\n",[33,2031,2032,2035],{"class":35,"line":429},[33,2033,2034],{"class":164},"      throw",[33,2036,2037],{"class":172}," error\n",[33,2039,2040],{"class":35,"line":435},[33,2041,499],{"class":172},[33,2043,2044,2046,2049,2052],{"class":35,"line":441},[33,2045,315],{"class":203},[33,2047,2048],{"class":172},".props.",[33,2050,2051],{"class":168},"onError",[33,2053,2054],{"class":172},"?.(error, errorInfo)\n",[33,2056,2057],{"class":35,"line":450},[33,2058,335],{"class":172},[33,2060,2061],{"class":35,"line":455},[33,2062,65],{"emptyLinePlaceholder":64},[33,2064,2065,2067],{"class":35,"line":460},[33,2066,463],{"class":168},[33,2068,466],{"class":172},[33,2070,2071,2073,2075,2077,2080,2083,2085,2088,2090,2093],{"class":35,"line":469},[33,2072,472],{"class":164},[33,2074,475],{"class":172},[33,2076,478],{"class":203},[33,2078,2079],{"class":172},".state.hasError ",[33,2081,2082],{"class":164},"&&",[33,2084,490],{"class":203},[33,2086,2087],{"class":172},".state.error ",[33,2089,2005],{"class":164},[33,2091,2092],{"class":168}," ValidationError",[33,2094,302],{"class":172},[33,2096,2097,2099,2101],{"class":35,"line":484},[33,2098,487],{"class":164},[33,2100,490],{"class":203},[33,2102,493],{"class":172},[33,2104,2105],{"class":35,"line":496},[33,2106,499],{"class":172},[33,2108,2109,2111,2113],{"class":35,"line":502},[33,2110,379],{"class":164},[33,2112,490],{"class":203},[33,2114,509],{"class":172},[33,2116,2117],{"class":35,"line":512},[33,2118,335],{"class":172},[33,2120,2121],{"class":35,"line":517},[33,2122,227],{"class":172},[33,2124,2125],{"class":35,"line":522},[33,2126,65],{"emptyLinePlaceholder":64},[33,2128,2129],{"class":35,"line":527},[33,2130,2131],{"class":39},"\u002F\u002F App-level boundary catches only the re-thrown (unexpected) errors:\n",[33,2133,2134,2136,2138],{"class":35,"line":533},[33,2135,165],{"class":164},[33,2137,538],{"class":168},[33,2139,466],{"class":172},[33,2141,2142,2144],{"class":35,"line":543},[33,2143,185],{"class":164},[33,2145,548],{"class":172},[33,2147,2148,2150,2152,2154,2156,2158,2160],{"class":35,"line":551},[33,2149,554],{"class":172},[33,2151,557],{"class":203},[33,2153,560],{"class":168},[33,2155,321],{"class":164},[33,2157,565],{"class":172},[33,2159,1643],{"class":203},[33,2161,1646],{"class":172},[33,2163,2164,2166,2169,2171,2173,2175,2178],{"class":35,"line":579},[33,2165,582],{"class":172},[33,2167,2168],{"class":203},"SmartBoundary",[33,2170,560],{"class":168},[33,2172,321],{"class":164},[33,2174,565],{"class":172},[33,2176,2177],{"class":203},"FieldError",[33,2179,1646],{"class":172},[33,2181,2182,2185,2188],{"class":35,"line":591},[33,2183,2184],{"class":172},"        \u003C",[33,2186,2187],{"class":203},"Form",[33,2189,588],{"class":172},[33,2191,2192,2195,2197],{"class":35,"line":601},[33,2193,2194],{"class":172},"      \u003C\u002F",[33,2196,2168],{"class":203},[33,2198,212],{"class":172},[33,2200,2201,2203,2205],{"class":35,"line":607},[33,2202,594],{"class":172},[33,2204,557],{"class":203},[33,2206,212],{"class":172},[33,2208,2209],{"class":35,"line":1567},[33,2210,604],{"class":172},[33,2212,2213],{"class":35,"line":1575},[33,2214,227],{"class":172},[14,2216,2218],{"id":2217},"a-reusable-error-boundary-with-reset-capability","A Reusable Error Boundary with Reset Capability",[19,2220,2222],{"filename":2221,"language":22},"resettable_boundary.js",[24,2223,2225],{"className":26,"code":2224,"language":22,"meta":28,"style":28},"class ErrorBoundary extends Component {\n  state = { hasError: false, error: null }\n\n  static getDerivedStateFromError(error) {\n    return { hasError: true, error }\n  }\n\n  componentDidCatch(error, errorInfo) {\n    this.props.onError?.(error, errorInfo)\n  }\n\n  handleReset = () => {\n    \u002F\u002F Reset the error state → React re-renders children fresh.\n    \u002F\u002F CAVEAT: if the PROPS that caused the error haven't changed, the same\n    \u002F\u002F error will throw again immediately. See the key-based reset below.\n    this.setState({ hasError: false, error: null })\n  }\n\n  render() {\n    if (this.state.hasError) {\n      \u002F\u002F Support both: a React element fallback, or a render-prop fallback\n      \u002F\u002F that receives the error and a reset function for \"Try again\" UI.\n      if (typeof this.props.fallback === 'function') {\n        return this.props.fallback(this.state.error, this.handleReset)\n      }\n      return this.props.fallback\n    }\n    return this.props.children\n  }\n}\n\n\u002F\u002F Usage with a render-prop fallback:\nfunction ProductPage({ productId }) {\n  return (\n    \u003CErrorBoundary\n      onError={(error, info) => logErrorToService(error, info.componentStack)}\n      fallback={(error, reset) => (\n        \u003Cdiv className=\"error-panel\">\n          \u003Cp>Couldn't load this product.\u003C\u002Fp>\n          \u003Cbutton onClick={reset}>Try again\u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      )}\n    >\n      \u003CProductDetails productId={productId} \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n}\n",[30,2226,2227,2239,2255,2259,2271,2281,2285,2289,2303,2313,2317,2321,2334,2339,2344,2349,2368,2372,2376,2382,2392,2397,2402,2425,2448,2453,2461,2465,2473,2477,2481,2485,2490,2504,2510,2517,2545,2567,2584,2598,2615,2624,2629,2635,2651,2660,2665],{"__ignoreMap":28},[33,2228,2229,2231,2233,2235,2237],{"class":35,"line":36},[33,2230,277],{"class":164},[33,2232,280],{"class":168},[33,2234,283],{"class":164},[33,2236,286],{"class":168},[33,2238,289],{"class":172},[33,2240,2241,2243,2245,2247,2249,2251,2253],{"class":35,"line":43},[33,2242,1929],{"class":176},[33,2244,1347],{"class":164},[33,2246,324],{"class":172},[33,2248,327],{"class":203},[33,2250,1302],{"class":172},[33,2252,1110],{"class":203},[33,2254,330],{"class":172},[33,2256,2257],{"class":35,"line":49},[33,2258,65],{"emptyLinePlaceholder":64},[33,2260,2261,2263,2265,2267,2269],{"class":35,"line":55},[33,2262,344],{"class":164},[33,2264,347],{"class":168},[33,2266,200],{"class":172},[33,2268,352],{"class":176},[33,2270,302],{"class":172},[33,2272,2273,2275,2277,2279],{"class":35,"line":61},[33,2274,379],{"class":164},[33,2276,324],{"class":172},[33,2278,384],{"class":203},[33,2280,1968],{"class":172},[33,2282,2283],{"class":35,"line":68},[33,2284,335],{"class":172},[33,2286,2287],{"class":35,"line":74},[33,2288,65],{"emptyLinePlaceholder":64},[33,2290,2291,2293,2295,2297,2299,2301],{"class":35,"line":80},[33,2292,402],{"class":168},[33,2294,200],{"class":172},[33,2296,352],{"class":176},[33,2298,409],{"class":172},[33,2300,412],{"class":176},[33,2302,302],{"class":172},[33,2304,2305,2307,2309,2311],{"class":35,"line":86},[33,2306,315],{"class":203},[33,2308,2048],{"class":172},[33,2310,2051],{"class":168},[33,2312,2054],{"class":172},[33,2314,2315],{"class":35,"line":92},[33,2316,335],{"class":172},[33,2318,2319],{"class":35,"line":98},[33,2320,65],{"emptyLinePlaceholder":64},[33,2322,2323,2326,2328,2330,2332],{"class":35,"line":103},[33,2324,2325],{"class":168},"  handleReset",[33,2327,1347],{"class":164},[33,2329,1545],{"class":172},[33,2331,951],{"class":164},[33,2333,289],{"class":172},[33,2335,2336],{"class":35,"line":109},[33,2337,2338],{"class":39},"    \u002F\u002F Reset the error state → React re-renders children fresh.\n",[33,2340,2341],{"class":35,"line":115},[33,2342,2343],{"class":39},"    \u002F\u002F CAVEAT: if the PROPS that caused the error haven't changed, the same\n",[33,2345,2346],{"class":35,"line":389},[33,2347,2348],{"class":39},"    \u002F\u002F error will throw again immediately. See the key-based reset below.\n",[33,2350,2351,2353,2355,2357,2360,2362,2364,2366],{"class":35,"line":394},[33,2352,315],{"class":203},[33,2354,1428],{"class":172},[33,2356,1282],{"class":168},[33,2358,2359],{"class":172},"({ hasError: ",[33,2361,327],{"class":203},[33,2363,1302],{"class":172},[33,2365,1110],{"class":203},[33,2367,1307],{"class":172},[33,2369,2370],{"class":35,"line":399},[33,2371,335],{"class":172},[33,2373,2374],{"class":35,"line":417},[33,2375,65],{"emptyLinePlaceholder":64},[33,2377,2378,2380],{"class":35,"line":423},[33,2379,463],{"class":168},[33,2381,466],{"class":172},[33,2383,2384,2386,2388,2390],{"class":35,"line":429},[33,2385,472],{"class":164},[33,2387,475],{"class":172},[33,2389,478],{"class":203},[33,2391,481],{"class":172},[33,2393,2394],{"class":35,"line":435},[33,2395,2396],{"class":39},"      \u002F\u002F Support both: a React element fallback, or a render-prop fallback\n",[33,2398,2399],{"class":35,"line":441},[33,2400,2401],{"class":39},"      \u002F\u002F that receives the error and a reset function for \"Try again\" UI.\n",[33,2403,2404,2407,2409,2412,2414,2417,2420,2423],{"class":35,"line":450},[33,2405,2406],{"class":164},"      if",[33,2408,475],{"class":172},[33,2410,2411],{"class":164},"typeof",[33,2413,490],{"class":203},[33,2415,2416],{"class":172},".props.fallback ",[33,2418,2419],{"class":164},"===",[33,2421,2422],{"class":267}," 'function'",[33,2424,302],{"class":172},[33,2426,2427,2429,2431,2433,2436,2438,2440,2443,2445],{"class":35,"line":455},[33,2428,1441],{"class":164},[33,2430,490],{"class":203},[33,2432,2048],{"class":172},[33,2434,2435],{"class":168},"fallback",[33,2437,200],{"class":172},[33,2439,478],{"class":203},[33,2441,2442],{"class":172},".state.error, ",[33,2444,478],{"class":203},[33,2446,2447],{"class":172},".handleReset)\n",[33,2449,2450],{"class":35,"line":460},[33,2451,2452],{"class":172},"      }\n",[33,2454,2455,2457,2459],{"class":35,"line":469},[33,2456,487],{"class":164},[33,2458,490],{"class":203},[33,2460,493],{"class":172},[33,2462,2463],{"class":35,"line":484},[33,2464,499],{"class":172},[33,2466,2467,2469,2471],{"class":35,"line":496},[33,2468,379],{"class":164},[33,2470,490],{"class":203},[33,2472,509],{"class":172},[33,2474,2475],{"class":35,"line":502},[33,2476,335],{"class":172},[33,2478,2479],{"class":35,"line":512},[33,2480,227],{"class":172},[33,2482,2483],{"class":35,"line":517},[33,2484,65],{"emptyLinePlaceholder":64},[33,2486,2487],{"class":35,"line":522},[33,2488,2489],{"class":39},"\u002F\u002F Usage with a render-prop fallback:\n",[33,2491,2492,2494,2497,2499,2502],{"class":35,"line":527},[33,2493,165],{"class":164},[33,2495,2496],{"class":168}," ProductPage",[33,2498,173],{"class":172},[33,2500,2501],{"class":176},"productId",[33,2503,180],{"class":172},[33,2505,2506,2508],{"class":35,"line":533},[33,2507,185],{"class":164},[33,2509,548],{"class":172},[33,2511,2512,2514],{"class":35,"line":543},[33,2513,554],{"class":172},[33,2515,2516],{"class":203},"ErrorBoundary\n",[33,2518,2519,2522,2524,2527,2529,2531,2534,2537,2539,2542],{"class":35,"line":551},[33,2520,2521],{"class":168},"      onError",[33,2523,321],{"class":164},[33,2525,2526],{"class":172},"{(",[33,2528,352],{"class":176},[33,2530,409],{"class":172},[33,2532,2533],{"class":176},"info",[33,2535,2536],{"class":172},") ",[33,2538,951],{"class":164},[33,2540,2541],{"class":168}," logErrorToService",[33,2543,2544],{"class":172},"(error, info.componentStack)}\n",[33,2546,2547,2550,2552,2554,2556,2558,2561,2563,2565],{"class":35,"line":579},[33,2548,2549],{"class":168},"      fallback",[33,2551,321],{"class":164},[33,2553,2526],{"class":172},[33,2555,352],{"class":176},[33,2557,409],{"class":172},[33,2559,2560],{"class":176},"reset",[33,2562,2536],{"class":172},[33,2564,951],{"class":164},[33,2566,548],{"class":172},[33,2568,2569,2571,2574,2577,2579,2582],{"class":35,"line":591},[33,2570,2184],{"class":172},[33,2572,2573],{"class":191},"div",[33,2575,2576],{"class":168}," className",[33,2578,321],{"class":164},[33,2580,2581],{"class":267},"\"error-panel\"",[33,2583,212],{"class":172},[33,2585,2586,2589,2591,2594,2596],{"class":35,"line":601},[33,2587,2588],{"class":172},"          \u003C",[33,2590,568],{"class":191},[33,2592,2593],{"class":172},">Couldn't load this product.\u003C\u002F",[33,2595,568],{"class":191},[33,2597,212],{"class":172},[33,2599,2600,2602,2604,2606,2608,2611,2613],{"class":35,"line":607},[33,2601,2588],{"class":172},[33,2603,901],{"class":191},[33,2605,904],{"class":168},[33,2607,321],{"class":164},[33,2609,2610],{"class":172},"{reset}>Try again\u003C\u002F",[33,2612,901],{"class":191},[33,2614,212],{"class":172},[33,2616,2617,2620,2622],{"class":35,"line":1567},[33,2618,2619],{"class":172},"        \u003C\u002F",[33,2621,2573],{"class":191},[33,2623,212],{"class":172},[33,2625,2626],{"class":35,"line":1575},[33,2627,2628],{"class":172},"      )}\n",[33,2630,2632],{"class":35,"line":2631},43,[33,2633,2634],{"class":172},"    >\n",[33,2636,2638,2640,2643,2646,2648],{"class":35,"line":2637},44,[33,2639,582],{"class":172},[33,2641,2642],{"class":203},"ProductDetails",[33,2644,2645],{"class":168}," productId",[33,2647,321],{"class":164},[33,2649,2650],{"class":172},"{productId} \u002F>\n",[33,2652,2654,2656,2658],{"class":35,"line":2653},45,[33,2655,594],{"class":172},[33,2657,557],{"class":203},[33,2659,212],{"class":172},[33,2661,2663],{"class":35,"line":2662},46,[33,2664,604],{"class":172},[33,2666,2668],{"class":35,"line":2667},47,[33,2669,227],{"class":172},[611,2671,2673],{"id":2672},"reset-with-key-based-remount","Reset with Key-Based Remount",[19,2675,2677],{"filename":2676,"language":22},"key_reset.js",[24,2678,2680],{"className":26,"code":2679,"language":22,"meta":28,"style":28},"\u002F\u002F Resetting hasError alone re-renders the same children with the same props —\n\u002F\u002F if the error was caused by a prop value, the identical error throws again.\n\u002F\u002F A more complete reset changes a `key` on the boundary tied to whatever input\n\u002F\u002F might have changed, forcing a FULL remount rather than re-rendering broken state.\n\nfunction ProductPage({ productId }) {\n  const [resetKey, setResetKey] = useState(0)\n\n  return (\n    \u003CErrorBoundary\n      key={resetKey}  \u002F\u002F changing key → full remount → fresh state everywhere\n      fallback={(error, reset) => (\n        \u003Cdiv className=\"error-panel\">\n          \u003Cp>Couldn't load this product.\u003C\u002Fp>\n          \u003Cbutton onClick={() => {\n            setResetKey(k => k + 1)  \u002F\u002F change the key → remount\n            reset()                   \u002F\u002F also reset boundary state (belt + suspenders)\n          }}>\n            Try again\n          \u003C\u002Fbutton>\n        \u003C\u002Fdiv>\n      )}\n    >\n      \u003CProductDetails productId={productId} \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n}\n\n\u002F\u002F The key change forces React to unmount and recreate the entire subtree, giving\n\u002F\u002F every child a fresh state — useful when the error was caused by corrupted\n\u002F\u002F internal state that a simple re-render wouldn't fix.\n",[30,2681,2682,2687,2692,2697,2702,2706,2718,2745,2749,2755,2761,2774,2794,2808,2820,2836,2863,2874,2879,2884,2893,2901,2905,2909,2921,2929,2933,2937,2941,2946,2951],{"__ignoreMap":28},[33,2683,2684],{"class":35,"line":36},[33,2685,2686],{"class":39},"\u002F\u002F Resetting hasError alone re-renders the same children with the same props —\n",[33,2688,2689],{"class":35,"line":43},[33,2690,2691],{"class":39},"\u002F\u002F if the error was caused by a prop value, the identical error throws again.\n",[33,2693,2694],{"class":35,"line":49},[33,2695,2696],{"class":39},"\u002F\u002F A more complete reset changes a `key` on the boundary tied to whatever input\n",[33,2698,2699],{"class":35,"line":55},[33,2700,2701],{"class":39},"\u002F\u002F might have changed, forcing a FULL remount rather than re-rendering broken state.\n",[33,2703,2704],{"class":35,"line":61},[33,2705,65],{"emptyLinePlaceholder":64},[33,2707,2708,2710,2712,2714,2716],{"class":35,"line":68},[33,2709,165],{"class":164},[33,2711,2496],{"class":168},[33,2713,173],{"class":172},[33,2715,2501],{"class":176},[33,2717,180],{"class":172},[33,2719,2720,2722,2724,2727,2729,2732,2734,2736,2738,2740,2743],{"class":35,"line":74},[33,2721,1087],{"class":164},[33,2723,1090],{"class":172},[33,2725,2726],{"class":203},"resetKey",[33,2728,409],{"class":172},[33,2730,2731],{"class":203},"setResetKey",[33,2733,1100],{"class":172},[33,2735,321],{"class":164},[33,2737,1105],{"class":168},[33,2739,200],{"class":172},[33,2741,2742],{"class":203},"0",[33,2744,873],{"class":172},[33,2746,2747],{"class":35,"line":80},[33,2748,65],{"emptyLinePlaceholder":64},[33,2750,2751,2753],{"class":35,"line":86},[33,2752,185],{"class":164},[33,2754,548],{"class":172},[33,2756,2757,2759],{"class":35,"line":92},[33,2758,554],{"class":172},[33,2760,2516],{"class":203},[33,2762,2763,2766,2768,2771],{"class":35,"line":98},[33,2764,2765],{"class":168},"      key",[33,2767,321],{"class":164},[33,2769,2770],{"class":172},"{resetKey}  ",[33,2772,2773],{"class":39},"\u002F\u002F changing key → full remount → fresh state everywhere\n",[33,2775,2776,2778,2780,2782,2784,2786,2788,2790,2792],{"class":35,"line":103},[33,2777,2549],{"class":168},[33,2779,321],{"class":164},[33,2781,2526],{"class":172},[33,2783,352],{"class":176},[33,2785,409],{"class":172},[33,2787,2560],{"class":176},[33,2789,2536],{"class":172},[33,2791,951],{"class":164},[33,2793,548],{"class":172},[33,2795,2796,2798,2800,2802,2804,2806],{"class":35,"line":109},[33,2797,2184],{"class":172},[33,2799,2573],{"class":191},[33,2801,2576],{"class":168},[33,2803,321],{"class":164},[33,2805,2581],{"class":267},[33,2807,212],{"class":172},[33,2809,2810,2812,2814,2816,2818],{"class":35,"line":115},[33,2811,2588],{"class":172},[33,2813,568],{"class":191},[33,2815,2593],{"class":172},[33,2817,568],{"class":191},[33,2819,212],{"class":172},[33,2821,2822,2824,2826,2828,2830,2832,2834],{"class":35,"line":389},[33,2823,2588],{"class":172},[33,2825,901],{"class":191},[33,2827,904],{"class":168},[33,2829,321],{"class":164},[33,2831,1211],{"class":172},[33,2833,951],{"class":164},[33,2835,289],{"class":172},[33,2837,2838,2841,2843,2846,2848,2851,2854,2857,2860],{"class":35,"line":394},[33,2839,2840],{"class":168},"            setResetKey",[33,2842,200],{"class":172},[33,2844,2845],{"class":176},"k",[33,2847,972],{"class":164},[33,2849,2850],{"class":172}," k ",[33,2852,2853],{"class":164},"+",[33,2855,2856],{"class":203}," 1",[33,2858,2859],{"class":172},")  ",[33,2861,2862],{"class":39},"\u002F\u002F change the key → remount\n",[33,2864,2865,2868,2871],{"class":35,"line":399},[33,2866,2867],{"class":168},"            reset",[33,2869,2870],{"class":172},"()                   ",[33,2872,2873],{"class":39},"\u002F\u002F also reset boundary state (belt + suspenders)\n",[33,2875,2876],{"class":35,"line":417},[33,2877,2878],{"class":172},"          }}>\n",[33,2880,2881],{"class":35,"line":423},[33,2882,2883],{"class":172},"            Try again\n",[33,2885,2886,2889,2891],{"class":35,"line":429},[33,2887,2888],{"class":172},"          \u003C\u002F",[33,2890,901],{"class":191},[33,2892,212],{"class":172},[33,2894,2895,2897,2899],{"class":35,"line":435},[33,2896,2619],{"class":172},[33,2898,2573],{"class":191},[33,2900,212],{"class":172},[33,2902,2903],{"class":35,"line":441},[33,2904,2628],{"class":172},[33,2906,2907],{"class":35,"line":450},[33,2908,2634],{"class":172},[33,2910,2911,2913,2915,2917,2919],{"class":35,"line":455},[33,2912,582],{"class":172},[33,2914,2642],{"class":203},[33,2916,2645],{"class":168},[33,2918,321],{"class":164},[33,2920,2650],{"class":172},[33,2922,2923,2925,2927],{"class":35,"line":460},[33,2924,594],{"class":172},[33,2926,557],{"class":203},[33,2928,212],{"class":172},[33,2930,2931],{"class":35,"line":469},[33,2932,604],{"class":172},[33,2934,2935],{"class":35,"line":484},[33,2936,227],{"class":172},[33,2938,2939],{"class":35,"line":496},[33,2940,65],{"emptyLinePlaceholder":64},[33,2942,2943],{"class":35,"line":502},[33,2944,2945],{"class":39},"\u002F\u002F The key change forces React to unmount and recreate the entire subtree, giving\n",[33,2947,2948],{"class":35,"line":512},[33,2949,2950],{"class":39},"\u002F\u002F every child a fresh state — useful when the error was caused by corrupted\n",[33,2952,2953],{"class":35,"line":517},[33,2954,2955],{"class":39},"\u002F\u002F internal state that a simple re-render wouldn't fix.\n",[14,2957,2959],{"id":2958},"logging-to-an-external-error-service","Logging to an External Error Service",[19,2961,2963],{"filename":2962,"language":22},"error_logging.js",[24,2964,2966],{"className":26,"code":2965,"language":22,"meta":28,"style":28},"\u002F\u002F Production error boundaries should report to an external service (Sentry,\n\u002F\u002F Datadog, Bugsnag, Rollbar) so you know about errors users encounter.\n\nimport * as Sentry from '@sentry\u002Freact'\n\nclass ReportingBoundary extends Component {\n  state = { hasError: false, error: null }\n\n  static getDerivedStateFromError(error) {\n    return { hasError: true, error }\n  }\n\n  componentDidCatch(error, errorInfo) {\n    \u002F\u002F Sentry captures the error with full component stack — this is the\n    \u002F\u002F MOST valuable field, since the raw JS stack trace often can't tell you\n    \u002F\u002F WHICH instance of a reused component actually failed.\n    Sentry.withScope(scope => {\n      scope.setExtra('componentStack', errorInfo.componentStack)\n      scope.setExtra('props', this.props)  \u002F\u002F avoid sensitive data in production\n      Sentry.captureException(error)\n    })\n  }\n\n  render() {\n    if (this.state.hasError) return this.props.fallback\n    return this.props.children\n  }\n}\n\n\u002F\u002F SENTRY-SPECIFIC: @sentry\u002Freact exports its own ErrorBoundary that does all of\n\u002F\u002F the above out of the box, plus automatic breadcrumb collection and replay:\nimport { ErrorBoundary as SentryBoundary } from '@sentry\u002Freact'\n\nfunction App() {\n  return (\n    \u003CSentryBoundary\n      fallback={({ resetError }) => \u003CErrorPage onReset={resetError} \u002F>}\n      beforeCapture={scope => scope.setTag('section', 'main')}\n    >\n      \u003CApp \u002F>\n    \u003C\u002FSentryBoundary>\n  )\n}\n",[30,2967,2968,2973,2978,2982,3000,3004,3017,3033,3037,3049,3059,3063,3067,3081,3086,3091,3096,3113,3129,3150,3161,3165,3169,3173,3179,3196,3204,3208,3212,3216,3221,3226,3243,3247,3255,3261,3268,3298,3330,3334,3343,3352,3356],{"__ignoreMap":28},[33,2969,2970],{"class":35,"line":36},[33,2971,2972],{"class":39},"\u002F\u002F Production error boundaries should report to an external service (Sentry,\n",[33,2974,2975],{"class":35,"line":43},[33,2976,2977],{"class":39},"\u002F\u002F Datadog, Bugsnag, Rollbar) so you know about errors users encounter.\n",[33,2979,2980],{"class":35,"line":49},[33,2981,65],{"emptyLinePlaceholder":64},[33,2983,2984,2986,2989,2992,2995,2997],{"class":35,"line":55},[33,2985,258],{"class":164},[33,2987,2988],{"class":203}," *",[33,2990,2991],{"class":164}," as",[33,2993,2994],{"class":172}," Sentry ",[33,2996,264],{"class":164},[33,2998,2999],{"class":267}," '@sentry\u002Freact'\n",[33,3001,3002],{"class":35,"line":61},[33,3003,65],{"emptyLinePlaceholder":64},[33,3005,3006,3008,3011,3013,3015],{"class":35,"line":68},[33,3007,277],{"class":164},[33,3009,3010],{"class":168}," ReportingBoundary",[33,3012,283],{"class":164},[33,3014,286],{"class":168},[33,3016,289],{"class":172},[33,3018,3019,3021,3023,3025,3027,3029,3031],{"class":35,"line":74},[33,3020,1929],{"class":176},[33,3022,1347],{"class":164},[33,3024,324],{"class":172},[33,3026,327],{"class":203},[33,3028,1302],{"class":172},[33,3030,1110],{"class":203},[33,3032,330],{"class":172},[33,3034,3035],{"class":35,"line":80},[33,3036,65],{"emptyLinePlaceholder":64},[33,3038,3039,3041,3043,3045,3047],{"class":35,"line":86},[33,3040,344],{"class":164},[33,3042,347],{"class":168},[33,3044,200],{"class":172},[33,3046,352],{"class":176},[33,3048,302],{"class":172},[33,3050,3051,3053,3055,3057],{"class":35,"line":92},[33,3052,379],{"class":164},[33,3054,324],{"class":172},[33,3056,384],{"class":203},[33,3058,1968],{"class":172},[33,3060,3061],{"class":35,"line":98},[33,3062,335],{"class":172},[33,3064,3065],{"class":35,"line":103},[33,3066,65],{"emptyLinePlaceholder":64},[33,3068,3069,3071,3073,3075,3077,3079],{"class":35,"line":109},[33,3070,402],{"class":168},[33,3072,200],{"class":172},[33,3074,352],{"class":176},[33,3076,409],{"class":172},[33,3078,412],{"class":176},[33,3080,302],{"class":172},[33,3082,3083],{"class":35,"line":115},[33,3084,3085],{"class":39},"    \u002F\u002F Sentry captures the error with full component stack — this is the\n",[33,3087,3088],{"class":35,"line":389},[33,3089,3090],{"class":39},"    \u002F\u002F MOST valuable field, since the raw JS stack trace often can't tell you\n",[33,3092,3093],{"class":35,"line":394},[33,3094,3095],{"class":39},"    \u002F\u002F WHICH instance of a reused component actually failed.\n",[33,3097,3098,3101,3104,3106,3109,3111],{"class":35,"line":399},[33,3099,3100],{"class":172},"    Sentry.",[33,3102,3103],{"class":168},"withScope",[33,3105,200],{"class":172},[33,3107,3108],{"class":176},"scope",[33,3110,972],{"class":164},[33,3112,289],{"class":172},[33,3114,3115,3118,3121,3123,3126],{"class":35,"line":417},[33,3116,3117],{"class":172},"      scope.",[33,3119,3120],{"class":168},"setExtra",[33,3122,200],{"class":172},[33,3124,3125],{"class":267},"'componentStack'",[33,3127,3128],{"class":172},", errorInfo.componentStack)\n",[33,3130,3131,3133,3135,3137,3140,3142,3144,3147],{"class":35,"line":423},[33,3132,3117],{"class":172},[33,3134,3120],{"class":168},[33,3136,200],{"class":172},[33,3138,3139],{"class":267},"'props'",[33,3141,409],{"class":172},[33,3143,478],{"class":203},[33,3145,3146],{"class":172},".props)  ",[33,3148,3149],{"class":39},"\u002F\u002F avoid sensitive data in production\n",[33,3151,3152,3155,3158],{"class":35,"line":429},[33,3153,3154],{"class":172},"      Sentry.",[33,3156,3157],{"class":168},"captureException",[33,3159,3160],{"class":172},"(error)\n",[33,3162,3163],{"class":35,"line":435},[33,3164,1011],{"class":172},[33,3166,3167],{"class":35,"line":441},[33,3168,335],{"class":172},[33,3170,3171],{"class":35,"line":450},[33,3172,65],{"emptyLinePlaceholder":64},[33,3174,3175,3177],{"class":35,"line":455},[33,3176,463],{"class":168},[33,3178,466],{"class":172},[33,3180,3181,3183,3185,3187,3190,3192,3194],{"class":35,"line":460},[33,3182,472],{"class":164},[33,3184,475],{"class":172},[33,3186,478],{"class":203},[33,3188,3189],{"class":172},".state.hasError) ",[33,3191,1190],{"class":164},[33,3193,490],{"class":203},[33,3195,493],{"class":172},[33,3197,3198,3200,3202],{"class":35,"line":469},[33,3199,379],{"class":164},[33,3201,490],{"class":203},[33,3203,509],{"class":172},[33,3205,3206],{"class":35,"line":484},[33,3207,335],{"class":172},[33,3209,3210],{"class":35,"line":496},[33,3211,227],{"class":172},[33,3213,3214],{"class":35,"line":502},[33,3215,65],{"emptyLinePlaceholder":64},[33,3217,3218],{"class":35,"line":512},[33,3219,3220],{"class":39},"\u002F\u002F SENTRY-SPECIFIC: @sentry\u002Freact exports its own ErrorBoundary that does all of\n",[33,3222,3223],{"class":35,"line":517},[33,3224,3225],{"class":39},"\u002F\u002F the above out of the box, plus automatic breadcrumb collection and replay:\n",[33,3227,3228,3230,3233,3236,3239,3241],{"class":35,"line":522},[33,3229,258],{"class":164},[33,3231,3232],{"class":172}," { ErrorBoundary ",[33,3234,3235],{"class":164},"as",[33,3237,3238],{"class":172}," SentryBoundary } ",[33,3240,264],{"class":164},[33,3242,2999],{"class":267},[33,3244,3245],{"class":35,"line":527},[33,3246,65],{"emptyLinePlaceholder":64},[33,3248,3249,3251,3253],{"class":35,"line":533},[33,3250,165],{"class":164},[33,3252,538],{"class":168},[33,3254,466],{"class":172},[33,3256,3257,3259],{"class":35,"line":543},[33,3258,185],{"class":164},[33,3260,548],{"class":172},[33,3262,3263,3265],{"class":35,"line":551},[33,3264,554],{"class":172},[33,3266,3267],{"class":203},"SentryBoundary\n",[33,3269,3270,3272,3274,3277,3280,3283,3285,3287,3290,3293,3295],{"class":35,"line":579},[33,3271,2549],{"class":168},[33,3273,321],{"class":164},[33,3275,3276],{"class":172},"{({ ",[33,3278,3279],{"class":176},"resetError",[33,3281,3282],{"class":172}," }) ",[33,3284,951],{"class":164},[33,3286,188],{"class":172},[33,3288,3289],{"class":203},"ErrorPage",[33,3291,3292],{"class":168}," onReset",[33,3294,321],{"class":164},[33,3296,3297],{"class":172},"{resetError} \u002F>}\n",[33,3299,3300,3303,3305,3307,3309,3311,3314,3317,3319,3322,3324,3327],{"class":35,"line":591},[33,3301,3302],{"class":168},"      beforeCapture",[33,3304,321],{"class":164},[33,3306,1033],{"class":172},[33,3308,3108],{"class":176},[33,3310,972],{"class":164},[33,3312,3313],{"class":172}," scope.",[33,3315,3316],{"class":168},"setTag",[33,3318,200],{"class":172},[33,3320,3321],{"class":267},"'section'",[33,3323,409],{"class":172},[33,3325,3326],{"class":267},"'main'",[33,3328,3329],{"class":172},")}\n",[33,3331,3332],{"class":35,"line":601},[33,3333,2634],{"class":172},[33,3335,3336,3338,3341],{"class":35,"line":607},[33,3337,582],{"class":172},[33,3339,3340],{"class":203},"App",[33,3342,588],{"class":172},[33,3344,3345,3347,3350],{"class":35,"line":1567},[33,3346,594],{"class":172},[33,3348,3349],{"class":203},"SentryBoundary",[33,3351,212],{"class":172},[33,3353,3354],{"class":35,"line":1575},[33,3355,604],{"class":172},[33,3357,3358],{"class":35,"line":2631},[33,3359,227],{"class":172},[14,3361,3363],{"id":3362},"fallback-ui-patterns","Fallback UI Patterns",[19,3365,3367],{"filename":3366,"language":22},"fallback_patterns.js",[24,3368,3370],{"className":26,"code":3369,"language":22,"meta":28,"style":28},"\u002F\u002F PATTERN 1: Static fallback (simplest — for non-critical regions)\n;\u003CErrorBoundary fallback={\u003Cp>Failed to load.\u003C\u002Fp>}>\n  \u003CWidget \u002F>\n\u003C\u002FErrorBoundary>\n\n\u002F\u002F PATTERN 2: Null fallback (silently hide broken non-essential widgets)\n;\u003CErrorBoundary fallback={null}>\n  \u003CAnalyticsWidget \u002F>\n\u003C\u002FErrorBoundary>\n\n\u002F\u002F PATTERN 3: Interactive fallback with retry (critical regions)\n;\u003CErrorBoundary\n  fallback={(error, reset) => (\n    \u003Cdiv role=\"alert\" className=\"error-panel\">\n      \u003Ch3>Something went wrong\u003C\u002Fh3>\n      \u003Cp>{error.message}\u003C\u002Fp>\n      \u003Cbutton onClick={reset}>Try again\u003C\u002Fbutton>\n      \u003Cbutton onClick={() => window.location.reload()}>Reload page\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )}\n>\n  \u003CCriticalContent \u002F>\n\u003C\u002FErrorBoundary>\n\n\u002F\u002F PATTERN 4: Render-prop fallback component (reusable, testable)\nfunction ErrorFallback({ error, resetErrorBoundary }) {\n  return (\n    \u003Cdiv role=\"alert\" className=\"error-panel\">\n      \u003Cp>Couldn't load this: {error.message}\u003C\u002Fp>\n      \u003Cbutton onClick={resetErrorBoundary}>Try again\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n\n;\u003CErrorBoundary FallbackComponent={ErrorFallback}>\n  \u003CContent \u002F>\n\u003C\u002FErrorBoundary>\n\n\u002F\u002F PATTERN 5: Context-aware fallback (access boundary state via render prop)\n;\u003CErrorBoundary\n  fallback={(error, reset) => {\n    if (error.name === 'ChunkLoadError') {\n      \u002F\u002F A lazy-loaded chunk failed to load (network issue, new deploy) —\n      \u002F\u002F reloading the page fetches fresh chunks, usually fixing it.\n      return (\n        \u003Cdiv>\n          \u003Cp>A new version is available. Reloading…\u003C\u002Fp>\n        \u003C\u002Fdiv>\n      )\n    }\n    return \u003CGenericError error={error} onRetry={reset} \u002F>\n  }}\n>\n  \u003CApp \u002F>\n\u003C\u002FErrorBoundary>\n",[30,3371,3372,3377,3395,3405,3414,3418,3423,3438,3447,3455,3459,3464,3472,3482,3500,3505,3510,3532,3552,3557,3562,3566,3575,3583,3587,3592,3610,3616,3637,3650,3667,3675,3679,3683,3687,3703,3712,3720,3724,3729,3737,3746,3760,3765,3770,3776,3784,3797,3806,3812,3817,3840,3846,3851,3860],{"__ignoreMap":28},[33,3373,3374],{"class":35,"line":36},[33,3375,3376],{"class":39},"\u002F\u002F PATTERN 1: Static fallback (simplest — for non-critical regions)\n",[33,3378,3379,3382,3385,3388,3390,3393],{"class":35,"line":43},[33,3380,3381],{"class":172},";",[33,3383,3384],{"class":164},"\u003C",[33,3386,3387],{"class":172},"ErrorBoundary fallback",[33,3389,321],{"class":164},[33,3391,3392],{"class":172},"{\u003Cp>Failed to load.\u003C\u002Fp>}",[33,3394,212],{"class":164},[33,3396,3397,3400,3403],{"class":35,"line":49},[33,3398,3399],{"class":172},"  \u003C",[33,3401,3402],{"class":203},"Widget",[33,3404,588],{"class":172},[33,3406,3407,3410,3412],{"class":35,"line":55},[33,3408,3409],{"class":164},"\u003C\u002F",[33,3411,557],{"class":172},[33,3413,212],{"class":164},[33,3415,3416],{"class":35,"line":61},[33,3417,65],{"emptyLinePlaceholder":64},[33,3419,3420],{"class":35,"line":68},[33,3421,3422],{"class":39},"\u002F\u002F PATTERN 2: Null fallback (silently hide broken non-essential widgets)\n",[33,3424,3425,3427,3429,3431,3433,3436],{"class":35,"line":74},[33,3426,3381],{"class":172},[33,3428,3384],{"class":164},[33,3430,3387],{"class":172},[33,3432,321],{"class":164},[33,3434,3435],{"class":172},"{null}",[33,3437,212],{"class":164},[33,3439,3440,3442,3445],{"class":35,"line":80},[33,3441,3399],{"class":172},[33,3443,3444],{"class":203},"AnalyticsWidget",[33,3446,588],{"class":172},[33,3448,3449,3451,3453],{"class":35,"line":86},[33,3450,3409],{"class":164},[33,3452,557],{"class":172},[33,3454,212],{"class":164},[33,3456,3457],{"class":35,"line":92},[33,3458,65],{"emptyLinePlaceholder":64},[33,3460,3461],{"class":35,"line":98},[33,3462,3463],{"class":39},"\u002F\u002F PATTERN 3: Interactive fallback with retry (critical regions)\n",[33,3465,3466,3468,3470],{"class":35,"line":103},[33,3467,3381],{"class":172},[33,3469,3384],{"class":164},[33,3471,2516],{"class":172},[33,3473,3474,3477,3479],{"class":35,"line":109},[33,3475,3476],{"class":172},"  fallback",[33,3478,321],{"class":164},[33,3480,3481],{"class":172},"{(error, reset) => (\n",[33,3483,3484,3487,3489,3492,3494,3496,3498],{"class":35,"line":115},[33,3485,3486],{"class":172},"    \u003Cdiv role",[33,3488,321],{"class":164},[33,3490,3491],{"class":267},"\"alert\"",[33,3493,2576],{"class":172},[33,3495,321],{"class":164},[33,3497,2581],{"class":267},[33,3499,212],{"class":164},[33,3501,3502],{"class":35,"line":389},[33,3503,3504],{"class":172},"      \u003Ch3>Something went wrong\u003C\u002Fh3>\n",[33,3506,3507],{"class":35,"line":394},[33,3508,3509],{"class":172},"      \u003Cp>{error.message}\u003C\u002Fp>\n",[33,3511,3512,3515,3517,3520,3523,3526,3528,3530],{"class":35,"line":399},[33,3513,3514],{"class":172},"      \u003Cbutton onClick",[33,3516,321],{"class":164},[33,3518,3519],{"class":172},"{reset}",[33,3521,3522],{"class":164},">",[33,3524,3525],{"class":172},"Try again",[33,3527,3409],{"class":164},[33,3529,901],{"class":172},[33,3531,212],{"class":164},[33,3533,3534,3536,3538,3541,3543,3546,3548,3550],{"class":35,"line":417},[33,3535,3514],{"class":172},[33,3537,321],{"class":164},[33,3539,3540],{"class":172},"{() => window.location.reload()}",[33,3542,3522],{"class":164},[33,3544,3545],{"class":172},"Reload page",[33,3547,3409],{"class":164},[33,3549,901],{"class":172},[33,3551,212],{"class":164},[33,3553,3554],{"class":35,"line":423},[33,3555,3556],{"class":172},"    \u003C\u002Fdiv>\n",[33,3558,3559],{"class":35,"line":429},[33,3560,3561],{"class":172},"  )}\n",[33,3563,3564],{"class":35,"line":435},[33,3565,212],{"class":164},[33,3567,3568,3570,3573],{"class":35,"line":441},[33,3569,3399],{"class":172},[33,3571,3572],{"class":203},"CriticalContent",[33,3574,588],{"class":172},[33,3576,3577,3579,3581],{"class":35,"line":450},[33,3578,3409],{"class":164},[33,3580,557],{"class":172},[33,3582,212],{"class":164},[33,3584,3585],{"class":35,"line":455},[33,3586,65],{"emptyLinePlaceholder":64},[33,3588,3589],{"class":35,"line":460},[33,3590,3591],{"class":39},"\u002F\u002F PATTERN 4: Render-prop fallback component (reusable, testable)\n",[33,3593,3594,3596,3599,3601,3603,3605,3608],{"class":35,"line":469},[33,3595,165],{"class":164},[33,3597,3598],{"class":168}," ErrorFallback",[33,3600,173],{"class":172},[33,3602,352],{"class":176},[33,3604,409],{"class":172},[33,3606,3607],{"class":176},"resetErrorBoundary",[33,3609,180],{"class":172},[33,3611,3612,3614],{"class":35,"line":484},[33,3613,185],{"class":164},[33,3615,548],{"class":172},[33,3617,3618,3620,3622,3625,3627,3629,3631,3633,3635],{"class":35,"line":496},[33,3619,554],{"class":172},[33,3621,2573],{"class":191},[33,3623,3624],{"class":168}," role",[33,3626,321],{"class":164},[33,3628,3491],{"class":267},[33,3630,2576],{"class":168},[33,3632,321],{"class":164},[33,3634,2581],{"class":267},[33,3636,212],{"class":172},[33,3638,3639,3641,3643,3646,3648],{"class":35,"line":502},[33,3640,582],{"class":172},[33,3642,568],{"class":191},[33,3644,3645],{"class":172},">Couldn't load this: {error.message}\u003C\u002F",[33,3647,568],{"class":191},[33,3649,212],{"class":172},[33,3651,3652,3654,3656,3658,3660,3663,3665],{"class":35,"line":512},[33,3653,582],{"class":172},[33,3655,901],{"class":191},[33,3657,904],{"class":168},[33,3659,321],{"class":164},[33,3661,3662],{"class":172},"{resetErrorBoundary}>Try again\u003C\u002F",[33,3664,901],{"class":191},[33,3666,212],{"class":172},[33,3668,3669,3671,3673],{"class":35,"line":517},[33,3670,594],{"class":172},[33,3672,2573],{"class":191},[33,3674,212],{"class":172},[33,3676,3677],{"class":35,"line":522},[33,3678,604],{"class":172},[33,3680,3681],{"class":35,"line":527},[33,3682,227],{"class":172},[33,3684,3685],{"class":35,"line":533},[33,3686,65],{"emptyLinePlaceholder":64},[33,3688,3689,3691,3693,3696,3698,3701],{"class":35,"line":543},[33,3690,3381],{"class":172},[33,3692,3384],{"class":164},[33,3694,3695],{"class":172},"ErrorBoundary FallbackComponent",[33,3697,321],{"class":164},[33,3699,3700],{"class":172},"{ErrorFallback}",[33,3702,212],{"class":164},[33,3704,3705,3707,3710],{"class":35,"line":551},[33,3706,3399],{"class":172},[33,3708,3709],{"class":203},"Content",[33,3711,588],{"class":172},[33,3713,3714,3716,3718],{"class":35,"line":579},[33,3715,3409],{"class":164},[33,3717,557],{"class":172},[33,3719,212],{"class":164},[33,3721,3722],{"class":35,"line":591},[33,3723,65],{"emptyLinePlaceholder":64},[33,3725,3726],{"class":35,"line":601},[33,3727,3728],{"class":39},"\u002F\u002F PATTERN 5: Context-aware fallback (access boundary state via render prop)\n",[33,3730,3731,3733,3735],{"class":35,"line":607},[33,3732,3381],{"class":172},[33,3734,3384],{"class":164},[33,3736,2516],{"class":172},[33,3738,3739,3741,3743],{"class":35,"line":1567},[33,3740,3476],{"class":172},[33,3742,321],{"class":164},[33,3744,3745],{"class":172},"{(error, reset) => {\n",[33,3747,3748,3750,3753,3755,3758],{"class":35,"line":1575},[33,3749,472],{"class":164},[33,3751,3752],{"class":172}," (error.name ",[33,3754,2419],{"class":164},[33,3756,3757],{"class":267}," 'ChunkLoadError'",[33,3759,302],{"class":172},[33,3761,3762],{"class":35,"line":2631},[33,3763,3764],{"class":39},"      \u002F\u002F A lazy-loaded chunk failed to load (network issue, new deploy) —\n",[33,3766,3767],{"class":35,"line":2637},[33,3768,3769],{"class":39},"      \u002F\u002F reloading the page fetches fresh chunks, usually fixing it.\n",[33,3771,3772,3774],{"class":35,"line":2653},[33,3773,487],{"class":164},[33,3775,548],{"class":172},[33,3777,3778,3780,3782],{"class":35,"line":2662},[33,3779,2184],{"class":172},[33,3781,2573],{"class":191},[33,3783,212],{"class":172},[33,3785,3786,3788,3790,3793,3795],{"class":35,"line":2667},[33,3787,2588],{"class":172},[33,3789,568],{"class":191},[33,3791,3792],{"class":172},">A new version is available. Reloading…\u003C\u002F",[33,3794,568],{"class":191},[33,3796,212],{"class":172},[33,3798,3800,3802,3804],{"class":35,"line":3799},48,[33,3801,2619],{"class":172},[33,3803,2573],{"class":191},[33,3805,212],{"class":172},[33,3807,3809],{"class":35,"line":3808},49,[33,3810,3811],{"class":172},"      )\n",[33,3813,3815],{"class":35,"line":3814},50,[33,3816,499],{"class":172},[33,3818,3820,3822,3824,3827,3829,3831,3833,3835,3837],{"class":35,"line":3819},51,[33,3821,379],{"class":164},[33,3823,188],{"class":172},[33,3825,3826],{"class":203},"GenericError",[33,3828,1198],{"class":168},[33,3830,321],{"class":164},[33,3832,1203],{"class":172},[33,3834,1206],{"class":168},[33,3836,321],{"class":164},[33,3838,3839],{"class":172},"{reset} \u002F>\n",[33,3841,3843],{"class":35,"line":3842},52,[33,3844,3845],{"class":172},"  }}\n",[33,3847,3849],{"class":35,"line":3848},53,[33,3850,212],{"class":164},[33,3852,3854,3856,3858],{"class":35,"line":3853},54,[33,3855,3399],{"class":172},[33,3857,3340],{"class":203},[33,3859,588],{"class":172},[33,3861,3863,3865,3867],{"class":35,"line":3862},55,[33,3864,3409],{"class":164},[33,3866,557],{"class":172},[33,3868,212],{"class":164},[14,3870,3872,3875],{"id":3871},"react-error-boundary-the-common-production-choice",[30,3873,3874],{},"react-error-boundary",": The Common Production Choice",[19,3877,3879],{"filename":3878,"language":22},"react_error_boundary.js",[24,3880,3882],{"className":26,"code":3881,"language":22,"meta":28,"style":28},"\u002F\u002F Because the class-component boilerplate is largely identical across projects,\n\u002F\u002F most production codebases use the `react-error-boundary` library — it wraps\n\u002F\u002F the same underlying class mechanism behind a function-component-friendly API,\n\u002F\u002F including a useErrorBoundary hook for triggering a boundary from event handlers.\n\nimport { ErrorBoundary, useErrorBoundary } from 'react-error-boundary'\n\nfunction ProductPage({ productId }) {\n  return (\n    \u003CErrorBoundary\n      FallbackComponent={ErrorFallback}\n      onError={(error, info) => logErrorToService(error, info.componentStack)}\n      onReset={() => {\n        \u002F\u002F Called when resetErrorBoundary is invoked — use to reset any state\n        \u002F\u002F that might have contributed to the error before re-rendering children.\n        queryClient.resetQueries()\n      }}\n      resetKeys={[productId]}  \u002F\u002F auto-reset when productId changes\n    >\n      \u003CProductDetails productId={productId} \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n}\n\nfunction ErrorFallback({ error, resetErrorBoundary }) {\n  return (\n    \u003Cdiv role=\"alert\" className=\"error-panel\">\n      \u003Cp>Couldn't load this product: {error.message}\u003C\u002Fp>\n      \u003Cbutton onClick={resetErrorBoundary}>Try again\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F useErrorBoundary: deliberately trigger a boundary from an event handler or\n\u002F\u002F async code — the one way to make a non-render-phase error reach a boundary.\nfunction DeleteButton() {\n  const { showBoundary } = useErrorBoundary()\n\n  async function handleClick() {\n    try {\n      await api.deleteItem()\n    } catch (err) {\n      \u002F\u002F This is inside an event handler (not caught by boundary normally) —\n      \u002F\u002F showBoundary manually pushes it to the nearest ancestor boundary,\n      \u002F\u002F triggering its fallback UI.\n      showBoundary(err)\n    }\n  }\n  return \u003Cbutton onClick={handleClick}>Delete\u003C\u002Fbutton>\n}\n",[30,3883,3884,3889,3894,3899,3904,3908,3920,3924,3936,3942,3948,3958,3980,3993,3998,4003,4013,4018,4031,4035,4047,4055,4059,4063,4067,4083,4089,4109,4122,4138,4146,4150,4154,4158,4163,4168,4177,4197,4201,4211,4217,4229,4237,4242,4247,4252,4259,4263,4267,4286],{"__ignoreMap":28},[33,3885,3886],{"class":35,"line":36},[33,3887,3888],{"class":39},"\u002F\u002F Because the class-component boilerplate is largely identical across projects,\n",[33,3890,3891],{"class":35,"line":43},[33,3892,3893],{"class":39},"\u002F\u002F most production codebases use the `react-error-boundary` library — it wraps\n",[33,3895,3896],{"class":35,"line":49},[33,3897,3898],{"class":39},"\u002F\u002F the same underlying class mechanism behind a function-component-friendly API,\n",[33,3900,3901],{"class":35,"line":55},[33,3902,3903],{"class":39},"\u002F\u002F including a useErrorBoundary hook for triggering a boundary from event handlers.\n",[33,3905,3906],{"class":35,"line":61},[33,3907,65],{"emptyLinePlaceholder":64},[33,3909,3910,3912,3915,3917],{"class":35,"line":68},[33,3911,258],{"class":164},[33,3913,3914],{"class":172}," { ErrorBoundary, useErrorBoundary } ",[33,3916,264],{"class":164},[33,3918,3919],{"class":267}," 'react-error-boundary'\n",[33,3921,3922],{"class":35,"line":74},[33,3923,65],{"emptyLinePlaceholder":64},[33,3925,3926,3928,3930,3932,3934],{"class":35,"line":80},[33,3927,165],{"class":164},[33,3929,2496],{"class":168},[33,3931,173],{"class":172},[33,3933,2501],{"class":176},[33,3935,180],{"class":172},[33,3937,3938,3940],{"class":35,"line":86},[33,3939,185],{"class":164},[33,3941,548],{"class":172},[33,3943,3944,3946],{"class":35,"line":92},[33,3945,554],{"class":172},[33,3947,2516],{"class":203},[33,3949,3950,3953,3955],{"class":35,"line":98},[33,3951,3952],{"class":168},"      FallbackComponent",[33,3954,321],{"class":164},[33,3956,3957],{"class":172},"{ErrorFallback}\n",[33,3959,3960,3962,3964,3966,3968,3970,3972,3974,3976,3978],{"class":35,"line":103},[33,3961,2521],{"class":168},[33,3963,321],{"class":164},[33,3965,2526],{"class":172},[33,3967,352],{"class":176},[33,3969,409],{"class":172},[33,3971,2533],{"class":176},[33,3973,2536],{"class":172},[33,3975,951],{"class":164},[33,3977,2541],{"class":168},[33,3979,2544],{"class":172},[33,3981,3982,3985,3987,3989,3991],{"class":35,"line":109},[33,3983,3984],{"class":168},"      onReset",[33,3986,321],{"class":164},[33,3988,1211],{"class":172},[33,3990,951],{"class":164},[33,3992,289],{"class":172},[33,3994,3995],{"class":35,"line":115},[33,3996,3997],{"class":39},"        \u002F\u002F Called when resetErrorBoundary is invoked — use to reset any state\n",[33,3999,4000],{"class":35,"line":389},[33,4001,4002],{"class":39},"        \u002F\u002F that might have contributed to the error before re-rendering children.\n",[33,4004,4005,4008,4011],{"class":35,"line":394},[33,4006,4007],{"class":172},"        queryClient.",[33,4009,4010],{"class":168},"resetQueries",[33,4012,1146],{"class":172},[33,4014,4015],{"class":35,"line":399},[33,4016,4017],{"class":172},"      }}\n",[33,4019,4020,4023,4025,4028],{"class":35,"line":417},[33,4021,4022],{"class":168},"      resetKeys",[33,4024,321],{"class":164},[33,4026,4027],{"class":172},"{[productId]}  ",[33,4029,4030],{"class":39},"\u002F\u002F auto-reset when productId changes\n",[33,4032,4033],{"class":35,"line":423},[33,4034,2634],{"class":172},[33,4036,4037,4039,4041,4043,4045],{"class":35,"line":429},[33,4038,582],{"class":172},[33,4040,2642],{"class":203},[33,4042,2645],{"class":168},[33,4044,321],{"class":164},[33,4046,2650],{"class":172},[33,4048,4049,4051,4053],{"class":35,"line":435},[33,4050,594],{"class":172},[33,4052,557],{"class":203},[33,4054,212],{"class":172},[33,4056,4057],{"class":35,"line":441},[33,4058,604],{"class":172},[33,4060,4061],{"class":35,"line":450},[33,4062,227],{"class":172},[33,4064,4065],{"class":35,"line":455},[33,4066,65],{"emptyLinePlaceholder":64},[33,4068,4069,4071,4073,4075,4077,4079,4081],{"class":35,"line":460},[33,4070,165],{"class":164},[33,4072,3598],{"class":168},[33,4074,173],{"class":172},[33,4076,352],{"class":176},[33,4078,409],{"class":172},[33,4080,3607],{"class":176},[33,4082,180],{"class":172},[33,4084,4085,4087],{"class":35,"line":469},[33,4086,185],{"class":164},[33,4088,548],{"class":172},[33,4090,4091,4093,4095,4097,4099,4101,4103,4105,4107],{"class":35,"line":484},[33,4092,554],{"class":172},[33,4094,2573],{"class":191},[33,4096,3624],{"class":168},[33,4098,321],{"class":164},[33,4100,3491],{"class":267},[33,4102,2576],{"class":168},[33,4104,321],{"class":164},[33,4106,2581],{"class":267},[33,4108,212],{"class":172},[33,4110,4111,4113,4115,4118,4120],{"class":35,"line":496},[33,4112,582],{"class":172},[33,4114,568],{"class":191},[33,4116,4117],{"class":172},">Couldn't load this product: {error.message}\u003C\u002F",[33,4119,568],{"class":191},[33,4121,212],{"class":172},[33,4123,4124,4126,4128,4130,4132,4134,4136],{"class":35,"line":502},[33,4125,582],{"class":172},[33,4127,901],{"class":191},[33,4129,904],{"class":168},[33,4131,321],{"class":164},[33,4133,3662],{"class":172},[33,4135,901],{"class":191},[33,4137,212],{"class":172},[33,4139,4140,4142,4144],{"class":35,"line":512},[33,4141,594],{"class":172},[33,4143,2573],{"class":191},[33,4145,212],{"class":172},[33,4147,4148],{"class":35,"line":517},[33,4149,604],{"class":172},[33,4151,4152],{"class":35,"line":522},[33,4153,227],{"class":172},[33,4155,4156],{"class":35,"line":527},[33,4157,65],{"emptyLinePlaceholder":64},[33,4159,4160],{"class":35,"line":533},[33,4161,4162],{"class":39},"\u002F\u002F useErrorBoundary: deliberately trigger a boundary from an event handler or\n",[33,4164,4165],{"class":35,"line":543},[33,4166,4167],{"class":39},"\u002F\u002F async code — the one way to make a non-render-phase error reach a boundary.\n",[33,4169,4170,4172,4175],{"class":35,"line":551},[33,4171,165],{"class":164},[33,4173,4174],{"class":168}," DeleteButton",[33,4176,466],{"class":172},[33,4178,4179,4181,4184,4187,4190,4192,4195],{"class":35,"line":579},[33,4180,1087],{"class":164},[33,4182,4183],{"class":172}," { ",[33,4185,4186],{"class":203},"showBoundary",[33,4188,4189],{"class":172}," } ",[33,4191,321],{"class":164},[33,4193,4194],{"class":168}," useErrorBoundary",[33,4196,1146],{"class":172},[33,4198,4199],{"class":35,"line":591},[33,4200,65],{"emptyLinePlaceholder":64},[33,4202,4203,4205,4207,4209],{"class":35,"line":601},[33,4204,1121],{"class":164},[33,4206,1124],{"class":164},[33,4208,852],{"class":168},[33,4210,466],{"class":172},[33,4212,4213,4215],{"class":35,"line":607},[33,4214,1133],{"class":164},[33,4216,289],{"class":172},[33,4218,4219,4221,4224,4227],{"class":35,"line":1567},[33,4220,1140],{"class":164},[33,4222,4223],{"class":172}," api.",[33,4225,4226],{"class":168},"deleteItem",[33,4228,1146],{"class":172},[33,4230,4231,4233,4235],{"class":35,"line":1575},[33,4232,1151],{"class":172},[33,4234,964],{"class":164},[33,4236,1156],{"class":172},[33,4238,4239],{"class":35,"line":2631},[33,4240,4241],{"class":39},"      \u002F\u002F This is inside an event handler (not caught by boundary normally) —\n",[33,4243,4244],{"class":35,"line":2637},[33,4245,4246],{"class":39},"      \u002F\u002F showBoundary manually pushes it to the nearest ancestor boundary,\n",[33,4248,4249],{"class":35,"line":2653},[33,4250,4251],{"class":39},"      \u002F\u002F triggering its fallback UI.\n",[33,4253,4254,4257],{"class":35,"line":2662},[33,4255,4256],{"class":168},"      showBoundary",[33,4258,999],{"class":172},[33,4260,4261],{"class":35,"line":2667},[33,4262,499],{"class":172},[33,4264,4265],{"class":35,"line":3799},[33,4266,335],{"class":172},[33,4268,4269,4271,4273,4275,4277,4279,4282,4284],{"class":35,"line":3808},[33,4270,185],{"class":164},[33,4272,188],{"class":172},[33,4274,901],{"class":191},[33,4276,904],{"class":168},[33,4278,321],{"class":164},[33,4280,4281],{"class":172},"{handleClick}>Delete\u003C\u002F",[33,4283,901],{"class":191},[33,4285,212],{"class":172},[33,4287,4288],{"class":35,"line":3814},[33,4289,227],{"class":172},[14,4291,4293],{"id":4292},"production-error-recovery-strategy","Production Error Recovery Strategy",[19,4295,4297],{"filename":4296,"language":22},"recovery_strategy.js",[24,4298,4300],{"className":26,"code":4299,"language":22,"meta":28,"style":28},"\u002F\u002F A complete production error recovery strategy layers multiple mechanisms:\n\n\u002F\u002F 1. App-level boundary: catches unexpected errors, shows full-page fallback\nfunction App() {\n  return (\n    \u003CErrorBoundary\n      FallbackComponent={FatalErrorFallback}\n      onError={(e, info) => Sentry.captureException(e, { extra: info })}\n    >\n      \u003CRouter>\n        \u003CRoutes \u002F>\n      \u003C\u002FRouter>\n    \u003C\u002FErrorBoundary>\n  )\n}\n\n\u002F\u002F 2. Route-level boundaries: one error in a route degrades only that route\nfunction Routes() {\n  return (\n    \u003CRoutes>\n      \u003CRoute path=\"\u002Fdashboard\" element={\n        \u003CErrorBoundary FallbackComponent={RouteErrorFallback}>\n          \u003CDashboard \u002F>\n        \u003C\u002FErrorBoundary>\n      } \u002F>\n      \u003CRoute path=\"\u002Fsettings\" element={\n        \u003CErrorBoundary FallbackComponent={RouteErrorFallback}>\n          \u003CSettings \u002F>\n        \u003C\u002FErrorBoundary>\n      } \u002F>\n    \u003C\u002FRoutes>\n  )\n}\n\n\u002F\u002F 3. Widget-level boundaries: non-critical widgets silently disappear\nfunction Dashboard() {\n  return (\n    \u003Cdiv>\n      \u003CErrorBoundary fallback={null}>\u003CAnalyticsChart \u002F>\u003C\u002FErrorBoundary>\n      \u003CErrorBoundary fallback={\u003Cp>Feed unavailable\u003C\u002Fp>}>\n        \u003CActivityFeed \u002F>\n      \u003C\u002FErrorBoundary>\n      \u003CCriticalActions \u002F>  {\u002F* no boundary — errors propagate to route boundary *\u002F}\n    \u003C\u002Fdiv>\n  )\n}\n\n\u002F\u002F 4. Lazy-load boundaries: catch chunk loading errors (new deploy, bad network)\nfunction LazyRoute() {\n  return (\n    \u003CErrorBoundary\n      fallback={(error) => {\n        if (error.name === 'ChunkLoadError') {\n          return \u003CReloadPrompt \u002F>\n        }\n        return \u003CGenericError error={error} \u002F>\n      }}\n    >\n      \u003CReact.Suspense fallback={\u003CSpinner \u002F>}>\n        \u003CLazyDashboard \u002F>\n      \u003C\u002FReact.Suspense>\n    \u003C\u002FErrorBoundary>\n  )\n}\n\n\u002F\u002F 5. Event handler errors: local try\u002Fcatch + useErrorBoundary for deliberate\n\u002F\u002F    boundary triggering when local recovery isn't possible\n",[30,4301,4302,4307,4311,4316,4324,4330,4336,4345,4372,4376,4385,4394,4402,4410,4414,4418,4422,4427,4436,4442,4450,4473,4487,4496,4504,4509,4528,4540,4549,4557,4561,4569,4573,4577,4581,4586,4595,4601,4609,4634,4655,4664,4672,4687,4695,4699,4703,4707,4712,4721,4727,4733,4749,4761,4773,4777,4793,4798,4803,4822,4832,4841,4850,4855,4860,4865,4871],{"__ignoreMap":28},[33,4303,4304],{"class":35,"line":36},[33,4305,4306],{"class":39},"\u002F\u002F A complete production error recovery strategy layers multiple mechanisms:\n",[33,4308,4309],{"class":35,"line":43},[33,4310,65],{"emptyLinePlaceholder":64},[33,4312,4313],{"class":35,"line":49},[33,4314,4315],{"class":39},"\u002F\u002F 1. App-level boundary: catches unexpected errors, shows full-page fallback\n",[33,4317,4318,4320,4322],{"class":35,"line":55},[33,4319,165],{"class":164},[33,4321,538],{"class":168},[33,4323,466],{"class":172},[33,4325,4326,4328],{"class":35,"line":61},[33,4327,185],{"class":164},[33,4329,548],{"class":172},[33,4331,4332,4334],{"class":35,"line":68},[33,4333,554],{"class":172},[33,4335,2516],{"class":203},[33,4337,4338,4340,4342],{"class":35,"line":74},[33,4339,3952],{"class":168},[33,4341,321],{"class":164},[33,4343,4344],{"class":172},"{FatalErrorFallback}\n",[33,4346,4347,4349,4351,4353,4356,4358,4360,4362,4364,4367,4369],{"class":35,"line":80},[33,4348,2521],{"class":168},[33,4350,321],{"class":164},[33,4352,2526],{"class":172},[33,4354,4355],{"class":176},"e",[33,4357,409],{"class":172},[33,4359,2533],{"class":176},[33,4361,2536],{"class":172},[33,4363,951],{"class":164},[33,4365,4366],{"class":172}," Sentry.",[33,4368,3157],{"class":168},[33,4370,4371],{"class":172},"(e, { extra: info })}\n",[33,4373,4374],{"class":35,"line":86},[33,4375,2634],{"class":172},[33,4377,4378,4380,4383],{"class":35,"line":92},[33,4379,582],{"class":172},[33,4381,4382],{"class":203},"Router",[33,4384,212],{"class":172},[33,4386,4387,4389,4392],{"class":35,"line":98},[33,4388,2184],{"class":172},[33,4390,4391],{"class":203},"Routes",[33,4393,588],{"class":172},[33,4395,4396,4398,4400],{"class":35,"line":103},[33,4397,2194],{"class":172},[33,4399,4382],{"class":203},[33,4401,212],{"class":172},[33,4403,4404,4406,4408],{"class":35,"line":109},[33,4405,594],{"class":172},[33,4407,557],{"class":203},[33,4409,212],{"class":172},[33,4411,4412],{"class":35,"line":115},[33,4413,604],{"class":172},[33,4415,4416],{"class":35,"line":389},[33,4417,227],{"class":172},[33,4419,4420],{"class":35,"line":394},[33,4421,65],{"emptyLinePlaceholder":64},[33,4423,4424],{"class":35,"line":399},[33,4425,4426],{"class":39},"\u002F\u002F 2. Route-level boundaries: one error in a route degrades only that route\n",[33,4428,4429,4431,4434],{"class":35,"line":417},[33,4430,165],{"class":164},[33,4432,4433],{"class":168}," Routes",[33,4435,466],{"class":172},[33,4437,4438,4440],{"class":35,"line":423},[33,4439,185],{"class":164},[33,4441,548],{"class":172},[33,4443,4444,4446,4448],{"class":35,"line":429},[33,4445,554],{"class":172},[33,4447,4391],{"class":203},[33,4449,212],{"class":172},[33,4451,4452,4454,4457,4460,4462,4465,4468,4470],{"class":35,"line":435},[33,4453,582],{"class":172},[33,4455,4456],{"class":203},"Route",[33,4458,4459],{"class":168}," path",[33,4461,321],{"class":164},[33,4463,4464],{"class":267},"\"\u002Fdashboard\"",[33,4466,4467],{"class":168}," element",[33,4469,321],{"class":164},[33,4471,4472],{"class":172},"{\n",[33,4474,4475,4477,4479,4482,4484],{"class":35,"line":441},[33,4476,2184],{"class":172},[33,4478,557],{"class":203},[33,4480,4481],{"class":168}," FallbackComponent",[33,4483,321],{"class":164},[33,4485,4486],{"class":172},"{RouteErrorFallback}>\n",[33,4488,4489,4491,4494],{"class":35,"line":450},[33,4490,2588],{"class":172},[33,4492,4493],{"class":203},"Dashboard",[33,4495,588],{"class":172},[33,4497,4498,4500,4502],{"class":35,"line":455},[33,4499,2619],{"class":172},[33,4501,557],{"class":203},[33,4503,212],{"class":172},[33,4505,4506],{"class":35,"line":460},[33,4507,4508],{"class":172},"      } \u002F>\n",[33,4510,4511,4513,4515,4517,4519,4522,4524,4526],{"class":35,"line":469},[33,4512,582],{"class":172},[33,4514,4456],{"class":203},[33,4516,4459],{"class":168},[33,4518,321],{"class":164},[33,4520,4521],{"class":267},"\"\u002Fsettings\"",[33,4523,4467],{"class":168},[33,4525,321],{"class":164},[33,4527,4472],{"class":172},[33,4529,4530,4532,4534,4536,4538],{"class":35,"line":484},[33,4531,2184],{"class":172},[33,4533,557],{"class":203},[33,4535,4481],{"class":168},[33,4537,321],{"class":164},[33,4539,4486],{"class":172},[33,4541,4542,4544,4547],{"class":35,"line":496},[33,4543,2588],{"class":172},[33,4545,4546],{"class":203},"Settings",[33,4548,588],{"class":172},[33,4550,4551,4553,4555],{"class":35,"line":502},[33,4552,2619],{"class":172},[33,4554,557],{"class":203},[33,4556,212],{"class":172},[33,4558,4559],{"class":35,"line":512},[33,4560,4508],{"class":172},[33,4562,4563,4565,4567],{"class":35,"line":517},[33,4564,594],{"class":172},[33,4566,4391],{"class":203},[33,4568,212],{"class":172},[33,4570,4571],{"class":35,"line":522},[33,4572,604],{"class":172},[33,4574,4575],{"class":35,"line":527},[33,4576,227],{"class":172},[33,4578,4579],{"class":35,"line":533},[33,4580,65],{"emptyLinePlaceholder":64},[33,4582,4583],{"class":35,"line":543},[33,4584,4585],{"class":39},"\u002F\u002F 3. Widget-level boundaries: non-critical widgets silently disappear\n",[33,4587,4588,4590,4593],{"class":35,"line":551},[33,4589,165],{"class":164},[33,4591,4592],{"class":168}," Dashboard",[33,4594,466],{"class":172},[33,4596,4597,4599],{"class":35,"line":579},[33,4598,185],{"class":164},[33,4600,548],{"class":172},[33,4602,4603,4605,4607],{"class":35,"line":591},[33,4604,554],{"class":172},[33,4606,2573],{"class":191},[33,4608,212],{"class":172},[33,4610,4611,4613,4615,4617,4619,4621,4623,4625,4628,4630,4632],{"class":35,"line":601},[33,4612,582],{"class":172},[33,4614,557],{"class":203},[33,4616,560],{"class":168},[33,4618,321],{"class":164},[33,4620,1033],{"class":172},[33,4622,1110],{"class":203},[33,4624,1821],{"class":172},[33,4626,4627],{"class":203},"AnalyticsChart",[33,4629,1750],{"class":172},[33,4631,557],{"class":203},[33,4633,212],{"class":172},[33,4635,4636,4638,4640,4642,4644,4646,4648,4651,4653],{"class":35,"line":607},[33,4637,582],{"class":172},[33,4639,557],{"class":203},[33,4641,560],{"class":168},[33,4643,321],{"class":164},[33,4645,565],{"class":172},[33,4647,568],{"class":191},[33,4649,4650],{"class":172},">Feed unavailable\u003C\u002F",[33,4652,568],{"class":191},[33,4654,576],{"class":172},[33,4656,4657,4659,4662],{"class":35,"line":1567},[33,4658,2184],{"class":172},[33,4660,4661],{"class":203},"ActivityFeed",[33,4663,588],{"class":172},[33,4665,4666,4668,4670],{"class":35,"line":1575},[33,4667,2194],{"class":172},[33,4669,557],{"class":203},[33,4671,212],{"class":172},[33,4673,4674,4676,4679,4682,4685],{"class":35,"line":2631},[33,4675,582],{"class":172},[33,4677,4678],{"class":203},"CriticalActions",[33,4680,4681],{"class":172}," \u002F>  {",[33,4683,4684],{"class":39},"\u002F* no boundary — errors propagate to route boundary *\u002F",[33,4686,227],{"class":172},[33,4688,4689,4691,4693],{"class":35,"line":2637},[33,4690,594],{"class":172},[33,4692,2573],{"class":191},[33,4694,212],{"class":172},[33,4696,4697],{"class":35,"line":2653},[33,4698,604],{"class":172},[33,4700,4701],{"class":35,"line":2662},[33,4702,227],{"class":172},[33,4704,4705],{"class":35,"line":2667},[33,4706,65],{"emptyLinePlaceholder":64},[33,4708,4709],{"class":35,"line":3799},[33,4710,4711],{"class":39},"\u002F\u002F 4. Lazy-load boundaries: catch chunk loading errors (new deploy, bad network)\n",[33,4713,4714,4716,4719],{"class":35,"line":3808},[33,4715,165],{"class":164},[33,4717,4718],{"class":168}," LazyRoute",[33,4720,466],{"class":172},[33,4722,4723,4725],{"class":35,"line":3814},[33,4724,185],{"class":164},[33,4726,548],{"class":172},[33,4728,4729,4731],{"class":35,"line":3819},[33,4730,554],{"class":172},[33,4732,2516],{"class":203},[33,4734,4735,4737,4739,4741,4743,4745,4747],{"class":35,"line":3842},[33,4736,2549],{"class":168},[33,4738,321],{"class":164},[33,4740,2526],{"class":172},[33,4742,352],{"class":176},[33,4744,2536],{"class":172},[33,4746,951],{"class":164},[33,4748,289],{"class":172},[33,4750,4751,4753,4755,4757,4759],{"class":35,"line":3848},[33,4752,1404],{"class":164},[33,4754,3752],{"class":172},[33,4756,2419],{"class":164},[33,4758,3757],{"class":267},[33,4760,302],{"class":172},[33,4762,4763,4766,4768,4771],{"class":35,"line":3853},[33,4764,4765],{"class":164},"          return",[33,4767,188],{"class":172},[33,4769,4770],{"class":203},"ReloadPrompt",[33,4772,588],{"class":172},[33,4774,4775],{"class":35,"line":3862},[33,4776,1534],{"class":172},[33,4778,4780,4782,4784,4786,4788,4790],{"class":35,"line":4779},56,[33,4781,1441],{"class":164},[33,4783,188],{"class":172},[33,4785,3826],{"class":203},[33,4787,1198],{"class":168},[33,4789,321],{"class":164},[33,4791,4792],{"class":172},"{error} \u002F>\n",[33,4794,4796],{"class":35,"line":4795},57,[33,4797,4017],{"class":172},[33,4799,4801],{"class":35,"line":4800},58,[33,4802,2634],{"class":172},[33,4804,4806,4808,4811,4813,4815,4817,4820],{"class":35,"line":4805},59,[33,4807,582],{"class":172},[33,4809,4810],{"class":203},"React.Suspense",[33,4812,560],{"class":168},[33,4814,321],{"class":164},[33,4816,565],{"class":172},[33,4818,4819],{"class":203},"Spinner",[33,4821,1646],{"class":172},[33,4823,4825,4827,4830],{"class":35,"line":4824},60,[33,4826,2184],{"class":172},[33,4828,4829],{"class":203},"LazyDashboard",[33,4831,588],{"class":172},[33,4833,4835,4837,4839],{"class":35,"line":4834},61,[33,4836,2194],{"class":172},[33,4838,4810],{"class":203},[33,4840,212],{"class":172},[33,4842,4844,4846,4848],{"class":35,"line":4843},62,[33,4845,594],{"class":172},[33,4847,557],{"class":203},[33,4849,212],{"class":172},[33,4851,4853],{"class":35,"line":4852},63,[33,4854,604],{"class":172},[33,4856,4858],{"class":35,"line":4857},64,[33,4859,227],{"class":172},[33,4861,4863],{"class":35,"line":4862},65,[33,4864,65],{"emptyLinePlaceholder":64},[33,4866,4868],{"class":35,"line":4867},66,[33,4869,4870],{"class":39},"\u002F\u002F 5. Event handler errors: local try\u002Fcatch + useErrorBoundary for deliberate\n",[33,4872,4874],{"class":35,"line":4873},67,[33,4875,4876],{"class":39},"\u002F\u002F    boundary triggering when local recovery isn't possible\n",[14,4878,4880],{"id":4879},"tips-tricks","💡 Tips & Tricks",[19,4882,4884],{"filename":4883,"language":22},"tips.js",[24,4885,4887],{"className":26,"code":4886,"language":22,"meta":28,"style":28},"\u002F\u002F [Idiom] Reach for `react-error-boundary` over a hand-rolled class in new\n\u002F\u002F projects — small, focused dependency, and the FallbackComponent\u002FonReset\u002F\n\u002F\u002F resetKeys\u002FuseErrorBoundary API covers reset-on-retry and manual-trigger\n\u002F\u002F needs that a bare class implementation requires hand-writing.\n\n\u002F\u002F [Idiom] Place boundaries at MEANINGFUL UI-region boundaries (a widget, a route,\n\u002F\u002F a card in a dashboard), not just at the app root — this keeps a rendering bug\n\u002F\u002F in one area from blanking an otherwise-functional page.\n\n\u002F\u002F [Debug] componentDidCatch's second argument, errorInfo.componentStack, is a\n\u002F\u002F string showing exactly which component tree the error propagated through —\n\u002F\u002F send this to your error service alongside the error. The plain JS stack trace\n\u002F\u002F alone often isn't enough to locate which INSTANCE of a reused component failed.\n\n\u002F\u002F [Idiom] When adding a \"Try again\" reset button, also change a `key` on the\n\u002F\u002F boundary tied to whatever input caused the failure — resetting hasError alone\n\u002F\u002F re-renders the same props that just threw, reproducing the identical error\n\u002F\u002F immediately if the cause wasn't transient.\n\n\u002F\u002F [Debug] To manually test a boundary's fallback UI during development, temporarily\n\u002F\u002F `throw` in a component's render body rather than relying on real error conditions\n\u002F\u002F to occur — reproducing the actual bug is often far more effort than verifying\n\u002F\u002F the boundary itself works.\n\n\u002F\u002F [Safety] Avoid putting sensitive data (tokens, PII) into error reports —\n\u002F\u002F componentDidCatch has access to this.props, but logging all props to an external\n\u002F\u002F service can leak user data. Filter or redact before sending.\n\n\u002F\u002F [Idiom] Use resetKeys prop (react-error-boundary) to auto-reset when relevant\n\u002F\u002F props change — e.g. resetKeys={[productId]} resets the boundary when the user\n\u002F\u002F navigates to a different product, without a manual \"Try again\" click.\n",[30,4888,4889,4894,4899,4904,4909,4913,4918,4923,4928,4932,4937,4942,4947,4952,4956,4961,4966,4971,4976,4980,4985,4990,4995,5000,5004,5009,5014,5019,5023,5028,5033],{"__ignoreMap":28},[33,4890,4891],{"class":35,"line":36},[33,4892,4893],{"class":39},"\u002F\u002F [Idiom] Reach for `react-error-boundary` over a hand-rolled class in new\n",[33,4895,4896],{"class":35,"line":43},[33,4897,4898],{"class":39},"\u002F\u002F projects — small, focused dependency, and the FallbackComponent\u002FonReset\u002F\n",[33,4900,4901],{"class":35,"line":49},[33,4902,4903],{"class":39},"\u002F\u002F resetKeys\u002FuseErrorBoundary API covers reset-on-retry and manual-trigger\n",[33,4905,4906],{"class":35,"line":55},[33,4907,4908],{"class":39},"\u002F\u002F needs that a bare class implementation requires hand-writing.\n",[33,4910,4911],{"class":35,"line":61},[33,4912,65],{"emptyLinePlaceholder":64},[33,4914,4915],{"class":35,"line":68},[33,4916,4917],{"class":39},"\u002F\u002F [Idiom] Place boundaries at MEANINGFUL UI-region boundaries (a widget, a route,\n",[33,4919,4920],{"class":35,"line":74},[33,4921,4922],{"class":39},"\u002F\u002F a card in a dashboard), not just at the app root — this keeps a rendering bug\n",[33,4924,4925],{"class":35,"line":80},[33,4926,4927],{"class":39},"\u002F\u002F in one area from blanking an otherwise-functional page.\n",[33,4929,4930],{"class":35,"line":86},[33,4931,65],{"emptyLinePlaceholder":64},[33,4933,4934],{"class":35,"line":92},[33,4935,4936],{"class":39},"\u002F\u002F [Debug] componentDidCatch's second argument, errorInfo.componentStack, is a\n",[33,4938,4939],{"class":35,"line":98},[33,4940,4941],{"class":39},"\u002F\u002F string showing exactly which component tree the error propagated through —\n",[33,4943,4944],{"class":35,"line":103},[33,4945,4946],{"class":39},"\u002F\u002F send this to your error service alongside the error. The plain JS stack trace\n",[33,4948,4949],{"class":35,"line":109},[33,4950,4951],{"class":39},"\u002F\u002F alone often isn't enough to locate which INSTANCE of a reused component failed.\n",[33,4953,4954],{"class":35,"line":115},[33,4955,65],{"emptyLinePlaceholder":64},[33,4957,4958],{"class":35,"line":389},[33,4959,4960],{"class":39},"\u002F\u002F [Idiom] When adding a \"Try again\" reset button, also change a `key` on the\n",[33,4962,4963],{"class":35,"line":394},[33,4964,4965],{"class":39},"\u002F\u002F boundary tied to whatever input caused the failure — resetting hasError alone\n",[33,4967,4968],{"class":35,"line":399},[33,4969,4970],{"class":39},"\u002F\u002F re-renders the same props that just threw, reproducing the identical error\n",[33,4972,4973],{"class":35,"line":417},[33,4974,4975],{"class":39},"\u002F\u002F immediately if the cause wasn't transient.\n",[33,4977,4978],{"class":35,"line":423},[33,4979,65],{"emptyLinePlaceholder":64},[33,4981,4982],{"class":35,"line":429},[33,4983,4984],{"class":39},"\u002F\u002F [Debug] To manually test a boundary's fallback UI during development, temporarily\n",[33,4986,4987],{"class":35,"line":435},[33,4988,4989],{"class":39},"\u002F\u002F `throw` in a component's render body rather than relying on real error conditions\n",[33,4991,4992],{"class":35,"line":441},[33,4993,4994],{"class":39},"\u002F\u002F to occur — reproducing the actual bug is often far more effort than verifying\n",[33,4996,4997],{"class":35,"line":450},[33,4998,4999],{"class":39},"\u002F\u002F the boundary itself works.\n",[33,5001,5002],{"class":35,"line":455},[33,5003,65],{"emptyLinePlaceholder":64},[33,5005,5006],{"class":35,"line":460},[33,5007,5008],{"class":39},"\u002F\u002F [Safety] Avoid putting sensitive data (tokens, PII) into error reports —\n",[33,5010,5011],{"class":35,"line":469},[33,5012,5013],{"class":39},"\u002F\u002F componentDidCatch has access to this.props, but logging all props to an external\n",[33,5015,5016],{"class":35,"line":484},[33,5017,5018],{"class":39},"\u002F\u002F service can leak user data. Filter or redact before sending.\n",[33,5020,5021],{"class":35,"line":496},[33,5022,65],{"emptyLinePlaceholder":64},[33,5024,5025],{"class":35,"line":502},[33,5026,5027],{"class":39},"\u002F\u002F [Idiom] Use resetKeys prop (react-error-boundary) to auto-reset when relevant\n",[33,5029,5030],{"class":35,"line":512},[33,5031,5032],{"class":39},"\u002F\u002F props change — e.g. resetKeys={[productId]} resets the boundary when the user\n",[33,5034,5035],{"class":35,"line":517},[33,5036,5037],{"class":39},"\u002F\u002F navigates to a different product, without a manual \"Try again\" click.\n",[14,5039,5041],{"id":5040},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[19,5043,5045],{"filename":5044,"language":22},"edge_cases.js",[24,5046,5048],{"className":26,"code":5047,"language":22,"meta":28,"style":28},"\u002F\u002F [Gotcha] Error boundaries NEVER catch errors from event handlers, async code,\n\u002F\u002F setTimeout callbacks, or SSR — only render-phase, lifecycle, and constructor\n\u002F\u002F errors in the tree below. Everything else needs local try\u002Fcatch or .catch().\n\n\u002F\u002F [Gotcha] A boundary CANNOT catch an error thrown within its OWN render method\n\u002F\u002F — the error propagates up to the NEXT boundary above it (or unmounts the tree\n\u002F\u002F if none). A top-level catch-all boundary still needs to exist even when\n\u002F\u002F granular boundaries are used elsewhere.\n\n\u002F\u002F [Gotcha] getDerivedStateFromError must be PURE — logging or reporting from\n\u002F\u002F inside it risks running multiple times (Strict Mode double-invocation in dev)\n\u002F\u002F or during a discarded render pass. Always use componentDidCatch for side effects.\n\n\u002F\u002F [Gotcha] Resetting a boundary's hasError state does NOT fix the underlying\n\u002F\u002F cause — if the error was caused by a prop that hasn't changed, clicking \"reset\"\n\u002F\u002F re-renders the same children with the same props, reproducing the identical\n\u002F\u002F error immediately. Use key-based remount or resetKeys to force a fresh state.\n\n\u002F\u002F [Gotcha] In development, React still logs errors caught by a boundary to the\n\u002F\u002F console (and may show an overlay) even though the fallback UI renders correctly\n\u002F\u002F — this is expected, not a sign the boundary failed. Production builds don't\n\u002F\u002F show the overlay.\n\n\u002F\u002F [Gotcha] componentDidCatch re-throws DO propagate to parent boundaries — but\n\u002F\u002F getDerivedStateFromError has already set hasError on the current boundary. If\n\u002F\u002F you re-throw from componentDidCatch, the parent boundary catches it, but the\n\u002F\u002F current boundary's state is already \"errored\" — the current boundary's fallback\n\u002F\u002F will NOT render (the re-throw bypasses it), only the parent's fallback shows.\n\n\u002F\u002F [Gotcha] Boundaries don't catch errors in non-React code — a Web Worker error,\n\u002F\u002F a WebSocket error, or a setTimeout callback error won't reach any boundary even\n\u002F\u002F if the callback was defined inside a component. Handle these at their source.\n\n\u002F\u002F [Gotcha] Lazy-loaded route components can throw ChunkLoadError when a new\n\u002F\u002F deploy invalidates old chunks — wrap Suspense in a boundary that detects\n\u002F\u002F ChunkLoadError and reloads the page to fetch fresh chunks.\n",[30,5049,5050,5055,5060,5065,5069,5074,5079,5084,5089,5093,5098,5103,5108,5112,5117,5122,5127,5132,5136,5141,5146,5151,5156,5160,5165,5170,5175,5180,5185,5189,5194,5199,5204,5208,5213,5218],{"__ignoreMap":28},[33,5051,5052],{"class":35,"line":36},[33,5053,5054],{"class":39},"\u002F\u002F [Gotcha] Error boundaries NEVER catch errors from event handlers, async code,\n",[33,5056,5057],{"class":35,"line":43},[33,5058,5059],{"class":39},"\u002F\u002F setTimeout callbacks, or SSR — only render-phase, lifecycle, and constructor\n",[33,5061,5062],{"class":35,"line":49},[33,5063,5064],{"class":39},"\u002F\u002F errors in the tree below. Everything else needs local try\u002Fcatch or .catch().\n",[33,5066,5067],{"class":35,"line":55},[33,5068,65],{"emptyLinePlaceholder":64},[33,5070,5071],{"class":35,"line":61},[33,5072,5073],{"class":39},"\u002F\u002F [Gotcha] A boundary CANNOT catch an error thrown within its OWN render method\n",[33,5075,5076],{"class":35,"line":68},[33,5077,5078],{"class":39},"\u002F\u002F — the error propagates up to the NEXT boundary above it (or unmounts the tree\n",[33,5080,5081],{"class":35,"line":74},[33,5082,5083],{"class":39},"\u002F\u002F if none). A top-level catch-all boundary still needs to exist even when\n",[33,5085,5086],{"class":35,"line":80},[33,5087,5088],{"class":39},"\u002F\u002F granular boundaries are used elsewhere.\n",[33,5090,5091],{"class":35,"line":86},[33,5092,65],{"emptyLinePlaceholder":64},[33,5094,5095],{"class":35,"line":92},[33,5096,5097],{"class":39},"\u002F\u002F [Gotcha] getDerivedStateFromError must be PURE — logging or reporting from\n",[33,5099,5100],{"class":35,"line":98},[33,5101,5102],{"class":39},"\u002F\u002F inside it risks running multiple times (Strict Mode double-invocation in dev)\n",[33,5104,5105],{"class":35,"line":103},[33,5106,5107],{"class":39},"\u002F\u002F or during a discarded render pass. Always use componentDidCatch for side effects.\n",[33,5109,5110],{"class":35,"line":109},[33,5111,65],{"emptyLinePlaceholder":64},[33,5113,5114],{"class":35,"line":115},[33,5115,5116],{"class":39},"\u002F\u002F [Gotcha] Resetting a boundary's hasError state does NOT fix the underlying\n",[33,5118,5119],{"class":35,"line":389},[33,5120,5121],{"class":39},"\u002F\u002F cause — if the error was caused by a prop that hasn't changed, clicking \"reset\"\n",[33,5123,5124],{"class":35,"line":394},[33,5125,5126],{"class":39},"\u002F\u002F re-renders the same children with the same props, reproducing the identical\n",[33,5128,5129],{"class":35,"line":399},[33,5130,5131],{"class":39},"\u002F\u002F error immediately. Use key-based remount or resetKeys to force a fresh state.\n",[33,5133,5134],{"class":35,"line":417},[33,5135,65],{"emptyLinePlaceholder":64},[33,5137,5138],{"class":35,"line":423},[33,5139,5140],{"class":39},"\u002F\u002F [Gotcha] In development, React still logs errors caught by a boundary to the\n",[33,5142,5143],{"class":35,"line":429},[33,5144,5145],{"class":39},"\u002F\u002F console (and may show an overlay) even though the fallback UI renders correctly\n",[33,5147,5148],{"class":35,"line":435},[33,5149,5150],{"class":39},"\u002F\u002F — this is expected, not a sign the boundary failed. Production builds don't\n",[33,5152,5153],{"class":35,"line":441},[33,5154,5155],{"class":39},"\u002F\u002F show the overlay.\n",[33,5157,5158],{"class":35,"line":450},[33,5159,65],{"emptyLinePlaceholder":64},[33,5161,5162],{"class":35,"line":455},[33,5163,5164],{"class":39},"\u002F\u002F [Gotcha] componentDidCatch re-throws DO propagate to parent boundaries — but\n",[33,5166,5167],{"class":35,"line":460},[33,5168,5169],{"class":39},"\u002F\u002F getDerivedStateFromError has already set hasError on the current boundary. If\n",[33,5171,5172],{"class":35,"line":469},[33,5173,5174],{"class":39},"\u002F\u002F you re-throw from componentDidCatch, the parent boundary catches it, but the\n",[33,5176,5177],{"class":35,"line":484},[33,5178,5179],{"class":39},"\u002F\u002F current boundary's state is already \"errored\" — the current boundary's fallback\n",[33,5181,5182],{"class":35,"line":496},[33,5183,5184],{"class":39},"\u002F\u002F will NOT render (the re-throw bypasses it), only the parent's fallback shows.\n",[33,5186,5187],{"class":35,"line":502},[33,5188,65],{"emptyLinePlaceholder":64},[33,5190,5191],{"class":35,"line":512},[33,5192,5193],{"class":39},"\u002F\u002F [Gotcha] Boundaries don't catch errors in non-React code — a Web Worker error,\n",[33,5195,5196],{"class":35,"line":517},[33,5197,5198],{"class":39},"\u002F\u002F a WebSocket error, or a setTimeout callback error won't reach any boundary even\n",[33,5200,5201],{"class":35,"line":522},[33,5202,5203],{"class":39},"\u002F\u002F if the callback was defined inside a component. Handle these at their source.\n",[33,5205,5206],{"class":35,"line":527},[33,5207,65],{"emptyLinePlaceholder":64},[33,5209,5210],{"class":35,"line":533},[33,5211,5212],{"class":39},"\u002F\u002F [Gotcha] Lazy-loaded route components can throw ChunkLoadError when a new\n",[33,5214,5215],{"class":35,"line":543},[33,5216,5217],{"class":39},"\u002F\u002F deploy invalidates old chunks — wrap Suspense in a boundary that detects\n",[33,5219,5220],{"class":35,"line":551},[33,5221,5222],{"class":39},"\u002F\u002F ChunkLoadError and reloads the page to fetch fresh chunks.\n",[14,5224,5226],{"id":5225},"spot-the-bug","🧠 Spot the Bug",[568,5228,5229],{},"A team wraps their whole app in an error boundary and is confused why a \"network request failed\" error from their data-fetching hook still crashes the page to a white screen instead of showing the fallback UI:",[19,5231,5233],{"filename":5232,"language":22},"spot_the_bug.js",[24,5234,5236],{"className":26,"code":5235,"language":22,"meta":28,"style":28},"function useProduct(id) {\n  const [product, setProduct] = useState(null)\n\n  useEffect(() => {\n    fetch(`\u002Fapi\u002Fproducts\u002F${id}`)\n      .then(res => res.json())\n      .then(setProduct)\n      .catch(err => {\n        throw err \u002F\u002F rethrow so \"the error boundary can catch it\"\n      })\n  }, [id])\n\n  return product\n}\n\nfunction App() {\n  return (\n    \u003CErrorBoundary fallback={\u003Cp>Something went wrong.\u003C\u002Fp>}>\n      \u003CProductDetails id=\"42\" \u002F>\n    \u003C\u002FErrorBoundary>\n  )\n}\n",[30,5237,5238,5252,5278,5282,5292,5307,5326,5335,5349,5360,5364,5369,5373,5380,5384,5388,5396,5402,5423,5439,5447,5451],{"__ignoreMap":28},[33,5239,5240,5242,5245,5247,5250],{"class":35,"line":36},[33,5241,165],{"class":164},[33,5243,5244],{"class":168}," useProduct",[33,5246,200],{"class":172},[33,5248,5249],{"class":176},"id",[33,5251,302],{"class":172},[33,5253,5254,5256,5258,5261,5263,5266,5268,5270,5272,5274,5276],{"class":35,"line":43},[33,5255,1087],{"class":164},[33,5257,1090],{"class":172},[33,5259,5260],{"class":203},"product",[33,5262,409],{"class":172},[33,5264,5265],{"class":203},"setProduct",[33,5267,1100],{"class":172},[33,5269,321],{"class":164},[33,5271,1105],{"class":168},[33,5273,200],{"class":172},[33,5275,1110],{"class":203},[33,5277,873],{"class":172},[33,5279,5280],{"class":35,"line":49},[33,5281,65],{"emptyLinePlaceholder":64},[33,5283,5284,5286,5288,5290],{"class":35,"line":55},[33,5285,945],{"class":168},[33,5287,948],{"class":172},[33,5289,951],{"class":164},[33,5291,289],{"class":172},[33,5293,5294,5296,5298,5301,5303,5305],{"class":35,"line":61},[33,5295,1379],{"class":168},[33,5297,200],{"class":172},[33,5299,5300],{"class":267},"`\u002Fapi\u002Fproducts\u002F${",[33,5302,5249],{"class":172},[33,5304,1434],{"class":267},[33,5306,873],{"class":172},[33,5308,5309,5311,5313,5315,5317,5319,5321,5323],{"class":35,"line":68},[33,5310,1387],{"class":172},[33,5312,1390],{"class":168},[33,5314,200],{"class":172},[33,5316,1395],{"class":176},[33,5318,972],{"class":164},[33,5320,1444],{"class":172},[33,5322,1447],{"class":168},[33,5324,5325],{"class":172},"())\n",[33,5327,5328,5330,5332],{"class":35,"line":74},[33,5329,1387],{"class":172},[33,5331,1390],{"class":168},[33,5333,5334],{"class":172},"(setProduct)\n",[33,5336,5337,5339,5341,5343,5345,5347],{"class":35,"line":80},[33,5338,1387],{"class":172},[33,5340,964],{"class":168},[33,5342,200],{"class":172},[33,5344,969],{"class":176},[33,5346,972],{"class":164},[33,5348,289],{"class":172},[33,5350,5351,5354,5357],{"class":35,"line":86},[33,5352,5353],{"class":164},"        throw",[33,5355,5356],{"class":172}," err ",[33,5358,5359],{"class":39},"\u002F\u002F rethrow so \"the error boundary can catch it\"\n",[33,5361,5362],{"class":35,"line":92},[33,5363,1454],{"class":172},[33,5365,5366],{"class":35,"line":98},[33,5367,5368],{"class":172},"  }, [id])\n",[33,5370,5371],{"class":35,"line":103},[33,5372,65],{"emptyLinePlaceholder":64},[33,5374,5375,5377],{"class":35,"line":109},[33,5376,185],{"class":164},[33,5378,5379],{"class":172}," product\n",[33,5381,5382],{"class":35,"line":115},[33,5383,227],{"class":172},[33,5385,5386],{"class":35,"line":389},[33,5387,65],{"emptyLinePlaceholder":64},[33,5389,5390,5392,5394],{"class":35,"line":394},[33,5391,165],{"class":164},[33,5393,538],{"class":168},[33,5395,466],{"class":172},[33,5397,5398,5400],{"class":35,"line":399},[33,5399,185],{"class":164},[33,5401,548],{"class":172},[33,5403,5404,5406,5408,5410,5412,5414,5416,5419,5421],{"class":35,"line":417},[33,5405,554],{"class":172},[33,5407,557],{"class":203},[33,5409,560],{"class":168},[33,5411,321],{"class":164},[33,5413,565],{"class":172},[33,5415,568],{"class":191},[33,5417,5418],{"class":172},">Something went wrong.\u003C\u002F",[33,5420,568],{"class":191},[33,5422,576],{"class":172},[33,5424,5425,5427,5429,5432,5434,5437],{"class":35,"line":423},[33,5426,582],{"class":172},[33,5428,2642],{"class":203},[33,5430,5431],{"class":168}," id",[33,5433,321],{"class":164},[33,5435,5436],{"class":267},"\"42\"",[33,5438,588],{"class":172},[33,5440,5441,5443,5445],{"class":35,"line":429},[33,5442,594],{"class":172},[33,5444,557],{"class":203},[33,5446,212],{"class":172},[33,5448,5449],{"class":35,"line":435},[33,5450,604],{"class":172},[33,5452,5453],{"class":35,"line":441},[33,5454,227],{"class":172},[5456,5457,5458,5462,5485,5491,5765],"details",{},[5459,5460,5461],"summary",{},"Answer",[568,5463,5464,5465,5468,5469,5472,5473,5477,5478,5481,5482,1428],{},"The ",[30,5466,5467],{},"throw err"," inside the ",[30,5470,5471],{},".catch()"," happens ",[5474,5475,5476],"strong",{},"asynchronously",", inside a promise callback that runs long after the component's render phase has finished and committed. By the time it executes, React is not \"in the middle of\" rendering this component at all. Throwing there produces an ",[5474,5479,5480],{},"unhandled promise rejection"," (visible in the console, and in some setups crashing the tab via the browser's unhandled-rejection behavior), but it never passes through React's error-boundary machinery, which only intercepts errors thrown ",[5474,5483,5484],{},"synchronously during render\u002Flifecycle\u002Fconstructor execution",[568,5486,5487,5490],{},[5474,5488,5489],{},"Fix",": handle the error at its source by setting local error state that the component reads during its next synchronous render:",[24,5492,5494],{"className":26,"code":5493,"language":22,"meta":28,"style":28},"function useProduct(id) {\n  const [state, setState] = useState({ status: 'loading', data: null, error: null })\n\n  useEffect(() => {\n    if (!id) return\n    const controller = new AbortController()\n    setState({ status: 'loading', data: null, error: null })\n    fetch(`\u002Fapi\u002Fproducts\u002F${id}`, { signal: controller.signal })\n      .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json() })\n      .then(data => setState({ status: 'success', data, error: null }))\n      .catch(err => {\n        if (err.name !== 'AbortError') setState({ status: 'error', data: null, error: err })\n      })\n    return () => controller.abort()\n  }, [id])\n\n  return state\n}\n",[30,5495,5496,5508,5540,5544,5554,5567,5581,5599,5614,5667,5691,5705,5729,5733,5747,5751,5755,5761],{"__ignoreMap":28},[33,5497,5498,5500,5502,5504,5506],{"class":35,"line":36},[33,5499,165],{"class":164},[33,5501,5244],{"class":168},[33,5503,200],{"class":172},[33,5505,5249],{"class":176},[33,5507,302],{"class":172},[33,5509,5510,5512,5514,5516,5518,5520,5522,5524,5526,5528,5530,5532,5534,5536,5538],{"class":35,"line":43},[33,5511,1087],{"class":164},[33,5513,1090],{"class":172},[33,5515,1277],{"class":203},[33,5517,409],{"class":172},[33,5519,1282],{"class":203},[33,5521,1100],{"class":172},[33,5523,321],{"class":164},[33,5525,1105],{"class":168},[33,5527,1291],{"class":172},[33,5529,1364],{"class":267},[33,5531,1297],{"class":172},[33,5533,1110],{"class":203},[33,5535,1302],{"class":172},[33,5537,1110],{"class":203},[33,5539,1307],{"class":172},[33,5541,5542],{"class":35,"line":49},[33,5543,65],{"emptyLinePlaceholder":64},[33,5545,5546,5548,5550,5552],{"class":35,"line":55},[33,5547,945],{"class":168},[33,5549,948],{"class":172},[33,5551,951],{"class":164},[33,5553,289],{"class":172},[33,5555,5556,5558,5560,5562,5565],{"class":35,"line":61},[33,5557,472],{"class":164},[33,5559,475],{"class":172},[33,5561,1330],{"class":164},[33,5563,5564],{"class":172},"id) ",[33,5566,1336],{"class":164},[33,5568,5569,5571,5573,5575,5577,5579],{"class":35,"line":68},[33,5570,1341],{"class":164},[33,5572,1344],{"class":203},[33,5574,1347],{"class":164},[33,5576,862],{"class":164},[33,5578,1352],{"class":168},[33,5580,1146],{"class":172},[33,5582,5583,5585,5587,5589,5591,5593,5595,5597],{"class":35,"line":74},[33,5584,1359],{"class":168},[33,5586,1291],{"class":172},[33,5588,1364],{"class":267},[33,5590,1297],{"class":172},[33,5592,1110],{"class":203},[33,5594,1302],{"class":172},[33,5596,1110],{"class":203},[33,5598,1307],{"class":172},[33,5600,5601,5603,5605,5607,5609,5611],{"class":35,"line":80},[33,5602,1379],{"class":168},[33,5604,200],{"class":172},[33,5606,5300],{"class":267},[33,5608,5249],{"class":172},[33,5610,1434],{"class":267},[33,5612,5613],{"class":172},", { signal: controller.signal })\n",[33,5615,5616,5618,5620,5622,5624,5626,5628,5631,5633,5635,5637,5639,5641,5643,5645,5647,5649,5651,5653,5655,5658,5660,5662,5664],{"class":35,"line":86},[33,5617,1387],{"class":172},[33,5619,1390],{"class":168},[33,5621,200],{"class":172},[33,5623,1395],{"class":176},[33,5625,972],{"class":164},[33,5627,4183],{"class":172},[33,5629,5630],{"class":164},"if",[33,5632,475],{"class":172},[33,5634,1330],{"class":164},[33,5636,1411],{"class":172},[33,5638,1414],{"class":164},[33,5640,862],{"class":164},[33,5642,865],{"class":168},[33,5644,200],{"class":172},[33,5646,1423],{"class":267},[33,5648,1395],{"class":172},[33,5650,1428],{"class":267},[33,5652,1431],{"class":172},[33,5654,1434],{"class":267},[33,5656,5657],{"class":172},"); ",[33,5659,1190],{"class":164},[33,5661,1444],{"class":172},[33,5663,1447],{"class":168},[33,5665,5666],{"class":172},"() })\n",[33,5668,5669,5671,5673,5675,5677,5679,5681,5683,5685,5687,5689],{"class":35,"line":92},[33,5670,1387],{"class":172},[33,5672,1390],{"class":168},[33,5674,200],{"class":172},[33,5676,1465],{"class":176},[33,5678,972],{"class":164},[33,5680,1470],{"class":168},[33,5682,1291],{"class":172},[33,5684,1475],{"class":267},[33,5686,1478],{"class":172},[33,5688,1110],{"class":203},[33,5690,1483],{"class":172},[33,5692,5693,5695,5697,5699,5701,5703],{"class":35,"line":98},[33,5694,1387],{"class":172},[33,5696,964],{"class":168},[33,5698,200],{"class":172},[33,5700,969],{"class":176},[33,5702,972],{"class":164},[33,5704,289],{"class":172},[33,5706,5707,5709,5711,5713,5715,5717,5719,5721,5723,5725,5727],{"class":35,"line":103},[33,5708,1404],{"class":164},[33,5710,1504],{"class":172},[33,5712,1507],{"class":164},[33,5714,1510],{"class":267},[33,5716,2536],{"class":172},[33,5718,1282],{"class":168},[33,5720,1291],{"class":172},[33,5722,1522],{"class":267},[33,5724,1297],{"class":172},[33,5726,1110],{"class":203},[33,5728,1529],{"class":172},[33,5730,5731],{"class":35,"line":109},[33,5732,1454],{"class":172},[33,5734,5735,5737,5739,5741,5743,5745],{"class":35,"line":115},[33,5736,379],{"class":164},[33,5738,1545],{"class":172},[33,5740,951],{"class":164},[33,5742,1550],{"class":172},[33,5744,1553],{"class":168},[33,5746,1146],{"class":172},[33,5748,5749],{"class":35,"line":389},[33,5750,5368],{"class":172},[33,5752,5753],{"class":35,"line":394},[33,5754,65],{"emptyLinePlaceholder":64},[33,5756,5757,5759],{"class":35,"line":399},[33,5758,185],{"class":164},[33,5760,1572],{"class":172},[33,5762,5763],{"class":35,"line":417},[33,5764,227],{"class":172},[568,5766,5767],{},"The lesson: error boundaries cannot catch asynchronous errors no matter how they're rethrown — async failures must be caught and handled at their actual source, typically by setting local error state that a component branches on during its next synchronous render.",[14,5769,5771],{"id":5770},"key-takeaways","Key Takeaways",[19,5773,5775],{"filename":5774,"language":22},"key_takeaways.js",[24,5776,5778],{"className":26,"code":5777,"language":22,"meta":28,"style":28},"\u002F\u002F 1. Error boundaries MUST be class components — getDerivedStateFromError and\n\u002F\u002F    componentDidCatch have no function-component equivalent. This is the one\n\u002F\u002F    deliberate, permanent exception to an all-function-components codebase.\n\n\u002F\u002F 2. getDerivedStateFromError (render phase, pure, returns state) triggers the\n\u002F\u002F    fallback. componentDidCatch (commit phase, impure, side effects) is for\n\u002F\u002F    logging. Never log inside getDerivedStateFromError.\n\n\u002F\u002F 3. Without any boundary, an uncaught render-phase error unmounts the ENTIRE\n\u002F\u002F    tree by design — leaving a corrupted UI is worse than showing nothing.\n\n\u002F\u002F 4. Boundaries catch ONLY render-phase, lifecycle, and constructor errors in\n\u002F\u002F    the tree below — NEVER event handlers, async code, setTimeout, or SSR.\n\u002F\u002F    Those need local try\u002Fcatch or .catch() handled at the source.\n\n\u002F\u002F 5. Prefer several GRANULAR boundaries around independent UI regions over one\n\u002F\u002F    boundary at the app root — a failure in one widget shouldn't blank the\n\u002F\u002F    entire page. But always keep a top-level catch-all as a safety net.\n\n\u002F\u002F 6. componentDidCatch's errorInfo.componentStack is valuable debugging context\n\u002F\u002F    — send it to your error-reporting service alongside the raw error.\n\n\u002F\u002F 7. Resetting hasError alone re-throws if the cause (props\u002Fstate) hasn't changed\n\u002F\u002F    — use key-based remount or react-error-boundary's resetKeys to force fresh state.\n\n\u002F\u002F 8. react-error-boundary wraps the class mechanism in a function-friendly API\n\u002F\u002F    and is the common production choice. Its useErrorBoundary hook can\n\u002F\u002F    deliberately trigger a boundary from event handlers\u002Fasync code.\n\n\u002F\u002F 9. A complete recovery strategy layers: app-level → route-level → widget-level\n\u002F\u002F    boundaries, plus chunk-load-error handling for lazy routes, plus local\n\u002F\u002F    try\u002Fcatch for event handlers.\n",[30,5779,5780,5785,5790,5795,5799,5804,5809,5814,5818,5823,5828,5832,5837,5842,5847,5851,5856,5861,5866,5870,5875,5880,5884,5889,5894,5898,5903,5908,5913,5917,5922,5927],{"__ignoreMap":28},[33,5781,5782],{"class":35,"line":36},[33,5783,5784],{"class":39},"\u002F\u002F 1. Error boundaries MUST be class components — getDerivedStateFromError and\n",[33,5786,5787],{"class":35,"line":43},[33,5788,5789],{"class":39},"\u002F\u002F    componentDidCatch have no function-component equivalent. This is the one\n",[33,5791,5792],{"class":35,"line":49},[33,5793,5794],{"class":39},"\u002F\u002F    deliberate, permanent exception to an all-function-components codebase.\n",[33,5796,5797],{"class":35,"line":55},[33,5798,65],{"emptyLinePlaceholder":64},[33,5800,5801],{"class":35,"line":61},[33,5802,5803],{"class":39},"\u002F\u002F 2. getDerivedStateFromError (render phase, pure, returns state) triggers the\n",[33,5805,5806],{"class":35,"line":68},[33,5807,5808],{"class":39},"\u002F\u002F    fallback. componentDidCatch (commit phase, impure, side effects) is for\n",[33,5810,5811],{"class":35,"line":74},[33,5812,5813],{"class":39},"\u002F\u002F    logging. Never log inside getDerivedStateFromError.\n",[33,5815,5816],{"class":35,"line":80},[33,5817,65],{"emptyLinePlaceholder":64},[33,5819,5820],{"class":35,"line":86},[33,5821,5822],{"class":39},"\u002F\u002F 3. Without any boundary, an uncaught render-phase error unmounts the ENTIRE\n",[33,5824,5825],{"class":35,"line":92},[33,5826,5827],{"class":39},"\u002F\u002F    tree by design — leaving a corrupted UI is worse than showing nothing.\n",[33,5829,5830],{"class":35,"line":98},[33,5831,65],{"emptyLinePlaceholder":64},[33,5833,5834],{"class":35,"line":103},[33,5835,5836],{"class":39},"\u002F\u002F 4. Boundaries catch ONLY render-phase, lifecycle, and constructor errors in\n",[33,5838,5839],{"class":35,"line":109},[33,5840,5841],{"class":39},"\u002F\u002F    the tree below — NEVER event handlers, async code, setTimeout, or SSR.\n",[33,5843,5844],{"class":35,"line":115},[33,5845,5846],{"class":39},"\u002F\u002F    Those need local try\u002Fcatch or .catch() handled at the source.\n",[33,5848,5849],{"class":35,"line":389},[33,5850,65],{"emptyLinePlaceholder":64},[33,5852,5853],{"class":35,"line":394},[33,5854,5855],{"class":39},"\u002F\u002F 5. Prefer several GRANULAR boundaries around independent UI regions over one\n",[33,5857,5858],{"class":35,"line":399},[33,5859,5860],{"class":39},"\u002F\u002F    boundary at the app root — a failure in one widget shouldn't blank the\n",[33,5862,5863],{"class":35,"line":417},[33,5864,5865],{"class":39},"\u002F\u002F    entire page. But always keep a top-level catch-all as a safety net.\n",[33,5867,5868],{"class":35,"line":423},[33,5869,65],{"emptyLinePlaceholder":64},[33,5871,5872],{"class":35,"line":429},[33,5873,5874],{"class":39},"\u002F\u002F 6. componentDidCatch's errorInfo.componentStack is valuable debugging context\n",[33,5876,5877],{"class":35,"line":435},[33,5878,5879],{"class":39},"\u002F\u002F    — send it to your error-reporting service alongside the raw error.\n",[33,5881,5882],{"class":35,"line":441},[33,5883,65],{"emptyLinePlaceholder":64},[33,5885,5886],{"class":35,"line":450},[33,5887,5888],{"class":39},"\u002F\u002F 7. Resetting hasError alone re-throws if the cause (props\u002Fstate) hasn't changed\n",[33,5890,5891],{"class":35,"line":455},[33,5892,5893],{"class":39},"\u002F\u002F    — use key-based remount or react-error-boundary's resetKeys to force fresh state.\n",[33,5895,5896],{"class":35,"line":460},[33,5897,65],{"emptyLinePlaceholder":64},[33,5899,5900],{"class":35,"line":469},[33,5901,5902],{"class":39},"\u002F\u002F 8. react-error-boundary wraps the class mechanism in a function-friendly API\n",[33,5904,5905],{"class":35,"line":484},[33,5906,5907],{"class":39},"\u002F\u002F    and is the common production choice. Its useErrorBoundary hook can\n",[33,5909,5910],{"class":35,"line":496},[33,5911,5912],{"class":39},"\u002F\u002F    deliberately trigger a boundary from event handlers\u002Fasync code.\n",[33,5914,5915],{"class":35,"line":502},[33,5916,65],{"emptyLinePlaceholder":64},[33,5918,5919],{"class":35,"line":512},[33,5920,5921],{"class":39},"\u002F\u002F 9. A complete recovery strategy layers: app-level → route-level → widget-level\n",[33,5923,5924],{"class":35,"line":517},[33,5925,5926],{"class":39},"\u002F\u002F    boundaries, plus chunk-load-error handling for lazy routes, plus local\n",[33,5928,5929],{"class":35,"line":522},[33,5930,5931],{"class":39},"\u002F\u002F    try\u002Fcatch for event handlers.\n",[5933,5934,5935],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}",{"title":28,"searchDepth":43,"depth":43,"links":5937},[5938,5939,5940,5944,5947,5950,5953,5954,5955,5957,5958,5959,5960,5961],{"id":16,"depth":43,"text":17},{"id":121,"depth":43,"text":122},{"id":244,"depth":43,"text":245,"children":5941},[5942],{"id":613,"depth":49,"text":5943},"getDerivedStateFromError vs componentDidCatch",{"id":778,"depth":43,"text":779,"children":5945},[5946],{"id":1051,"depth":49,"text":1052},{"id":1580,"depth":43,"text":1581,"children":5948},[5949],{"id":1869,"depth":49,"text":1870},{"id":2217,"depth":43,"text":2218,"children":5951},[5952],{"id":2672,"depth":49,"text":2673},{"id":2958,"depth":43,"text":2959},{"id":3362,"depth":43,"text":3363},{"id":3871,"depth":43,"text":5956},"react-error-boundary: The Common Production Choice",{"id":4292,"depth":43,"text":4293},{"id":4879,"depth":43,"text":4880},{"id":5040,"depth":43,"text":5041},{"id":5225,"depth":43,"text":5226},{"id":5770,"depth":43,"text":5771},"Error boundaries as the one deliberate class-component exception, getDerivedStateFromError vs componentDidCatch, what boundaries catch and don't catch, nesting and granularity, fallback UI patterns, reset strategies, re-throwing, logging to external services, and react-error-boundary. Code-first reference for mid-to-senior React engineers.","md",{},"\u002Freact\u002F16-error-boundaries",{"title":5,"description":5962},"react\u002F16-error-boundaries","OlvL_vWt2ZW2h6IBwafXZYiBnggdKS_sN4UMbjh8Sqw",1789924652367]