Error Boundaries: catching render errors
An error thrown during the rendering of a component, by default, unmounts the whole application (blank screen). An Error Boundary is a component that catches the errors of its children subtree and shows a fallback interface in its place, instead of taking down the whole app.
Error boundaries are one of the few cases where a class component is
still used, because they rely on lifecycle methods that have no
equivalent with hooks: getDerivedStateFromError and componentDidCatch:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
// Called when a child throws while rendering.
// Returns the new state to show the fallback.
static getDerivedStateFromError() {
return { hasError: true };
}
// Side effect: here you would log the error to a logging service.
componentDidCatch(error, info) {
console.error(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage: wrap the part you want to protect.
<ErrorBoundary>
<Profile />
</ErrorBoundary>;
Important: error boundaries do not catch errors in event handlers
(an onClick that throws), nor in asynchronous code, nor during
server-side rendering. Only errors that occur during the render of the
child components.
Compound components
Compound components are a pattern for designing
flexible and declarative APIs: a "parent" component manages the state and
several "children" collaborate with it, usually sharing data via context.
The user combines the pieces as if they were related HTML tags
(<select> and <option>):
<Tabs>
<Tab label="Home">Home content</Tab>
<Tab label="Settings">Settings content</Tab>
</Tabs>
Tabs controls which tab is active; each Tab renders or not depending on that
shared state. The advantage is an expressive and extensible API: whoever uses it
decides which tabs there are and in what order, without passing long configuration
lists through props.
Historical patterns: render props and HOC
Before hooks (React < 16.8), sharing logic between components was done with two patterns worth recognizing when reading old code:
Render props: a component receives a function as a prop (often
childrenitself) and passes it data so it decides what to paint:<MouseTracker> {({ x, y }) => <p>Cursor at {x}, {y}</p>} </MouseTracker>Higher-Order Component (HOC): a function that takes a component and returns another with added capabilities, like classic Redux's
connect(...):const WithData = withData(MyComponent);
Today, most of these cases are solved more simply with custom hooks, but render props and HOC still appear in libraries and existing codebases.