A caught error shows a fallback with a retry action. What must that reset actually do so the subtree does not fail again immediately?
answer
- what is different this time?
- clearing the flag is half a reset
- retry the work, or remount fresh
- cap attempts, then offer a way out
basics
~20 sClearing the boundary's caught state is not enough: the reset must change what produced the throw — re-run the failed work with fresh inputs, or remount the subtree under a new identity so no stale state survives.
solid answer
~50 sA reset has two halves and teams usually ship only the first. Half one: the boundary drops its caught state so it renders children again. Half two: something about those children must differ, or the same render throws and the fallback returns in a flicker. In practice the reset is paired with one of — **retry the work** that failed, so the child renders against a new result rather than the same bad one; **remount the subtree under a fresh identity**, discarding every instance and its local state; or **leave**, navigating to a view known to work. Add an attempt counter so the second or third failure escalates to a permanent fallback instead of looping, reset automatically on a signal that genuinely changed things — a navigation, a new identity for the data being viewed — and be careful retrying anything that may have partially completed.
go deeper
Take away the core idea: after an error is caught, something about the inputs or state has to change before rendering again, otherwise the same failure repeats straight away.
Explain the three moves and what each clears — retry the failed work, remount under a new identity, navigate away — and why resetting on every render loops.
Show that you have shipped this: attempt caps, meaningful reset triggers, backoff on automatic retries, caution about re-running work with side effects, and reporting every catch regardless.
Take the position on user outcomes: which surfaces may degrade in place, where a dead-end fallback is less honest than a crash, and what the minimum bar for any fallback in the product is.
## Why clearing the flag is not recovery When a boundary catches, it records that it is in a failed state and renders a fallback. "Reset" usually means clearing that record so the boundary renders its children again. But the children are a **pure function of the same inputs**: the same data in the same store, the same properties from the same parent, the same missing field. Render them again and they throw again, the boundary catches again, and the user sees a flicker between fallback and nothing. Worse, if the reset is wired to something that re-runs on its own, you get a loop that burns CPU and floods monitoring. So the useful question is not "how do I clear the error" but **"what is different this time?"** ## The three recovery moves 1. **Retry the failed work.** If the throw came from consuming a bad or missing result, re-run the operation that produced it and render again only when there is a new result. This is the only move that addresses the cause rather than the symptom, and it is the one users expect from a button labelled retry. 2. **Remount with a fresh identity.** Give the subtree a new identity so the runtime treats it as a different instance: every component below is created from scratch, with no local state carried over from the failed attempt. This is the blunt instrument that clears state you cannot enumerate — a half-initialised controller, a cache entry the component wrote, an index pointing past the end of a list. 3. **Leave.** Navigate to a known-good view, or reload the document. Reload is the crudest and, for a state-heavy long-lived page, sometimes the honest one: it discards everything, including whatever corruption caused the failure. Most fallbacks want the first plus a route to the third: retry, and if retry fails twice, offer the way out. | Move | Clears local state below? | Addresses the cause? | Typical use | |---|---|---|---| | Clear the caught flag only | sometimes, partially | no | almost never enough on its own | | Retry the failed work | no, by itself | yes, when the cause was the result | the default retry action | | Remount under a new identity | yes, entirely | only if state was the cause | stubborn or unknown failures | | Navigate away or reload | yes | resets everything, cause included | escape hatch after repeated failure | ## Guard rails a real fallback needs - **Count attempts.** After two or three failures, stop offering retry and say so. A button that never works is worse than no button. - **Reset on a meaningful change, not on every render.** A navigation to a different view, or a change in the identity of the thing being displayed, is evidence that conditions differ. A parent re-render is not. - **Do not auto-retry instantly.** If you retry without user action, back off between attempts, cap the attempts, and make the pending state visible. - **Beware retrying work with side effects.** Re-running something that may already have partially completed can duplicate its effect; retry the read, and treat anything that writes as needing its own idempotency story. - **Report every catch, including the ones a reset appears to fix.** A boundary that heals silently hides a defect that is still occurring. ## Why a dead-end fallback can be worse than a crash On a long-lived page — an editor, a console, a dashboard someone leaves open all day — an uncontained crash at least communicates unambiguously and points at the one action that works: reload. A fallback with no way forward communicates the opposite. The page still looks alive, the chrome still responds, so the user waits, clicks around, and eventually concludes the product is broken rather than that the view needs refreshing. Meanwhile any state the failure corrupted is still there, and the next interaction may fail somewhere else. That is not an argument against containment; it is an argument that **containment without a route out is only half the feature.** The minimum bar for any fallback is: it names what failed, it offers an action that can change the outcome, and after that action keeps failing it tells the user what to do instead. ## A note on where the reset lives The boundary owns the caught state, but usually it does **not** own the work that failed — that belongs to a child or to a shared store. So a fallback typically calls outward: it invokes a callback the boundary was given, which re-triggers the operation and then clears the caught state, or it changes the identity the subtree is rendered with. Designing that seam is the difference between a boundary that recovers and a boundary that only announces.
- What is a sensible automatic reset trigger?Something that genuinely changes the inputs: navigating to another view, or a change in the identity of the record being displayed. Both mean the next render is not the render that failed. Resetting on any parent re-render is the trap, because it re-runs the identical failing work and produces a loop.
- When is remounting under a fresh identity the right move rather than a retry?When you suspect the failure came from accumulated state inside the subtree rather than from its inputs — a partially initialised controller, an index out of range, a stale cached value. A retry leaves those in place; a new identity throws away every instance below and rebuilds them, at the cost of losing any state the user cared about.
- Should a boundary ever recover silently, with no visible fallback?Only if the user lost nothing and the catch is still reported. A silent remount of a decorative widget is fine; silently re-running work the user was in the middle of can duplicate effects or lose input. And unreported silent recovery is the worst case: the defect keeps happening and no one ever sees it.
Flipping the breaker back on with the shorted toaster still plugged in just trips it again. The reset has to include unplugging the toaster.
saying these in an interview costs you the question
- Thinks clearing the caught error is by itself a working retry
- Wires an automatic reset to every render and calls the resulting flicker a glitch
- Offers an unlimited retry button that can never succeed
- Assumes a remount and a retry recover the same things
- Believes a contained dead end is always better for the user than a crash