Hello, I'm Carson.
This article will explain the complete implementation logic of Error Boundaries in React.
A picture summarizes:

Here is a brief explanation of React workflow, which is useful later. There are three steps:
- Trigger update
- render phase: calculate the side effects caused by update
- commit phase: perform side effects in the host environment
There are many side effects, such as:
- Insert DOM node
- Execute useEffect callback
All right, let's get to the subject.
What is Error Boundaries
React provides two API s related to error handling:
- getDerivedStateFromError: a static method that provides an opportunity to render the fallback UI when an error occurs
- componentDidCatch: a component instance method that provides an opportunity to record error information when an error occurs
Classcomponents that use these two API s are often called Error Boundaries.
All errors occurring in the "React workflow" in the "descendant component" of Error Boundaries will be captured by Error Boundaries.
From the introduction at the beginning, "React workflow" refers to:
- render stage
- commit phase
Consider the following codes:
class ErrorBoundary extends Component { componentDidCatch(e) { console.warn(""An error occurred", e); } render() { return <div>{this.props.children}</div>; } } const App = () => ( <ErrorBoundary> <A><B/></A> <C/> <ErrorBoundary> )
A. As descendants of ErrorBoundary, B and C will be captured by the componentDidCatch method in ErrorBoundary when an error occurs in the "React workflow".
Step 1: catch errors
First, let's look at "when are errors in workflow captured".
The core code of the render phase is as follows. The errors will be handled by handleError:
do { try { // For concurrent updates, workLoopConcurrent workLoopSync(); break; } catch (thrownValue) { handleError(root, thrownValue); } } while (true);
The commit phase involves a lot of work, such as:
- componentDidMount/Update execution
- Bind / unbind ref
- Useeffect / uselayouteeffect callback and destroy execution
These tasks will be executed in the following form, and the errors will be handled by captureCommitPhaseError:
try { // ... perform a job } catch (error) { captureCommitPhaseError(fiber, fiber.return, error); }
Step 2: construct callback
It can be found that even without Error Boundaries, the errors in the workflow have been captured by React. The correct logic should be:
- If there are Error Boundaries, execute the corresponding API
- Throw the prompt message of React
- If there are no Error Boundaries, an "uncapped error" is thrown
Therefore, no matter handleError or captureCommitPhaseError, it will start from the parent node of the node where the error occurs, traverse up layer by layer to find the nearest Error Boundaries.
Once found, it constructs:
- callback for executing Error Boundaries API
- callback for "throw React prompt"

React error prompt information, including prompt and error stack
// ... Logic is pruned for readability function createClassErrorUpdate() { if (typeof getDerivedStateFromError === 'function') { // callback to execute getDerivedStateFromError update.payload = () => { return getDerivedStateFromError(error); }; // callback used to throw the React prompt update.callback = () => { logCapturedError(fiber, errorInfo); }; } if (inst !== null && typeof inst.componentDidCatch === 'function') { // callback for executing componentDidCatch update.callback = function callback() { this.componentDidCatch(error); }; } return update; }
If no Error Boundaries are found, continue to traverse up to the root node.
The following is constructed:
- callback for "throw uncapped error"
- callback for "throw React prompt"
// ... Logic is pruned for readability funffction createRootErrorUpdate() { // callback used to throw "uncapped error" and "prompt of React" update.callback = () => { onUncaughtError(error); logCapturedError(fiber, errorInfo); }; return update; }
Execute callback
When does the constructed callback execute?
In React, there are two API s for "execute user defined callback":
- For ClassComponent, this In setstate (new state, callback), both new state and callback parameters can pass Function as callback
Therefore, for Error Boundaries, it is equivalent to actively triggering an update:
this.setState(() => { // callback to execute getDerivedStateFromError }, () => { // callback for executing componentDidCatch // And a callback for throwing the React prompt })
- For the root node, execute reactdom The callback parameter in render (element, container, callback) can pass Function as callback
Therefore, for the case of "no Error Boundaries", it is equivalent to actively executing the following functions:
ReactDOM.render(element, container, () => { // callback used to throw "uncapped error" and "prompt of React" })
Therefore, the implementation of Error Boundaries can be regarded as a new function implemented by React using the existing API.
summary
People often ask: why do Hooks have no Error Boundaries?
It can be seen that the implementation of Error Boundaries relies on this Setstate can pass the feature of callback, and useState cannot be fully benchmarked for the time being.
Finally, I'll leave you an assignment. The official document [1] describes four cases of errors that will not be captured by Error Boundaries.
Using this knowledge, can you analyze why they are not captured?
reference material
[1] Official documents: https://reactjs.org/docs/error-boundaries.html#introducing-error-boundaries