Hello, everyone. I am Karsong.
This article explains the full implementation logic of Error Boundaries in React.
Summary in a picture:
Here’s a quick guide to the React workflow. There are three steps:
-
Triggered update
-
Render phase: Calculates the side effects that updates will cause
-
Commit phase: Side effects are performed in the host environment
There are many side effects, such as:
-
Insert DOM node
-
Execute the useEffect callback
All right, let’s get to the point.
Welcome to join the Human high quality front-end framework research group, Band fly
What are Error Boundaries
React provides two error-handling apis:
-
GetDerivedStateFromError: Static method that provides an opportunity to render the Fallback UI if an error occurs
-
ComponentDidCatch: Component instance method that provides an opportunity to log error information when an error has occurred
Classcomponents that use these two apis are often called Error Boundaries.
All errors in the React workflow that occur in the descendants of Error Boundaries component will be caught by Error Boundaries.
The React workflow refers to:
-
Render phase
-
The commit phase
Consider the following code:
class ErrorBoundary extends Component {
componentDidCatch(e) {
console.warn(" error ", e); }render() {
return <div>{this.props.children}</div>; }}const App = () = > (
<ErrorBoundary>
<A><B/></A>
<C/>
<ErrorBoundary>
)
Copy the code
A, B and C, as descendants of ErrorBoundary, will be captured by componentDidCatch method in ErrorBoundary when errors occur in React workflow.
Step 1: Catch errors
Let’s start by looking at when errors are caught in the workflow.
The core code for the Render phase is as follows, and errors that occur are handled by handleError:
do {
try {
// For concurrent updates, workLoopConcurrent
workLoopSync();
break;
} catch(thrownValue) { handleError(root, thrownValue); }}while (true);
Copy the code
The COMMIT phase involves a lot of work, such as:
-
ComponentDidMount/Update
-
Bind/unbind ref
-
UseEffect/useLayoutEffect callback and destroy
This work is performed as follows, with errors being handled by captureCommitPhaseError:
try {
/ /... Perform a task
} catch (error) {
captureCommitPhaseError(fiber, fiber.return, error);
}
Copy the code
Step 2: Construct the callback
You can see that even without Error Boundaries, the errors in the workflow have been caught by React. The correct logic would be:
-
If there are Error Boundaries, implement the API
-
The React prompt message is displayed
-
If no Error Boundaries exist, throw uncaught errors
So either handleError or captureCommitPhaseError starts at the parent of the node where the Error occurred and goes up layer by layer looking for the nearest Error Boundaries.
Once found, it constructs:
-
Callbacks for executing the Error Boundaries API
-
Callback for raising React prompts
/ /... The logic is abridged for readability
function createClassErrorUpdate() {
if (typeof getDerivedStateFromError === 'function') {
Callback for getDerivedStateFromError
update.payload = () = > {
return getDerivedStateFromError(error);
};
// Callback for 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;
}
Copy the code
If Error Boundaries are not found, continue up to the root node.
Construct:
-
Callback used to throw uncaught errors
-
Callback for raising React prompts
/ /... The logic is abridged for readability
funffction createRootErrorUpdate() {
// Callback for throwing "uncaught errors" and "React prompt messages"
update.callback = () = > {
onUncaughtError(error);
logCapturedError(fiber, errorInfo);
};
return update;
}
Copy the code
Implement the callback
When will the constructed callback be executed?
React has two apis that perform user – defined callbacks:
- for
ClassComponent
.this.setState(newState, callback)
In thenewState
andcallback
Parameters can be passedFunction
As acallback
So Error Boundaries automatically trigger an update:
this.setState(() = > {
Callback for getDerivedStateFromError
}, () = > {
Callback for executing componentDidCatch
// And the callback used to raise the React prompt
})
Copy the code
- For the root node, execute
ReactDOM.render(element, container, callback)
In thecallback
Parameter energy transferFunction
As acallback
So, in the case of no Error Boundaries, the following function is actively executed:
ReactDOM.render(element, container, () = > {
// Callback for throwing "uncaught errors" and "React prompt messages"
})
Copy the code
So the implementation of Error Boundaries can be thought of as new functionality implemented by React using an existing API.
conclusion
The question is often asked: Why do Hooks have no Error Boundaries?
As you can see, the Error Boundaries implementation takes advantage of the fact that this.setState can pass callbacks, but useState is not fully benchmarked yet.
Finally, for your homework, the four errors described in the official document will not be caught by Error Boundaries.
Using this knowledge, can you analyze why they are not captured?