Have you ever been in a situation in development where everything is set up and works properly, but the application crashes production because of a missed little error? Then the client calls you and tells you the whole screen goes white while clicking some button. The react error boundary has blessed us with precisely those scenarios.
What problem do you see in the following code?
const navList = [ { slug: 'home', id: 1, name: 'Home' }, { slug: 'profile', id: 2, name: 'My Profile' } ] const Navigation = () => { if (''.length < 0) { // 👇️ calling React hook conditionally useEffect(() => { console.log('hello world') }) } return ( <ul> {navList.map(nav => <li key={nav.id}>{nav.name}</li>)} </ul> ) }
If you run this code, it will crash because of breaking the rule of working on hooks. And the error in development might be in a console (Rendered more hooks than during the previous render). Or you might not get an error at all; it depends on the project’s configuration. These types of problems create a white screen on a production. To solve this problem smoothly, we can create an Error Boundary component that can solve all of the abovementioned issues. If you want to create such a component, it is necessary to consider the following:
- It should be a Class component.
- Must implement “getDerivedStatFromError” or “componentDidCatch”
Fortunately, the react-error-boundary package provides us with all the instruments necessary to fix the errors, so we no longer have to spend extra time writing it. So let’s add it to the project:
npm install react-error-boundary yarn add react-error-boundary
The easiest way to use this component is to wrap the whole project in it. We can also use it in any file and create components with different visuals and functionalities for different pages.
import { createRoot } from 'react-dom/client' import { ErrorBoundary } from 'react-error-boundary' const container = document.getElementById('root') const root = createRoot(container) function RuntimeErrorFallback(props) { const { resetErrorBoundary, error } = props return ( <div> <p>Found error:</p> <pre style={{ color: 'red' }}>{error.message}</pre> <button onClick={resetErrorBoundary}>Try again</button> <button onClick={() => { window.navigation.navigate('/') resetErrorBoundary() }} > Main Page </button > </div> ) } const App = () => ( <> <ErrorBoundary FallbackComponent={RuntimeErrorFallback}> <Navigation /> </ErrorBoundary> </> ) root.render(<App />)
This is the case when we place the whole application under one component. We can also put any component in its boundary, preventing us from crashing the entire application and ensuring replacing only the components with errors.
Let’s go through a more complex example:
const InputFallback = ({ error, resetErrorBoundary }) => { return ( <div> <p>Something went wrong:</p> <pre style={{ color: 'red' }}>{error.message}</pre> <button onClick={resetErrorBoundary}>Type another word</button> </div> ) } const HandleInput = ({ username }) => { if (username === "redberry") { throw new Error("🍓 Don’t Type Redberry to avoid error. 🍓"); } return `Hi ${username}`; } const App = () => { const [username, setUsername] = useState(""); const usernameRef = useRef(null); return ( <div> <label htmlFor="username">{`Username (don't type "Redberry"): `}</label> <input placeholder={`type "redberry"`} value={username} onChange={(e) => setUsername(e.target.value)} ref={usernameRef} id="username" /> <div> <ErrorBoundary FallbackComponent={InputFallback} onReset={() => { setUsername(""); usernameRef.current.focus(); }} resetKeys={[username]} > <HandleInput username={username} /> </ErrorBoundary> </div> </div> ); };
If you type “redberry,” the HandleInput component will be replaced with a component indicated in FallbackComponent. Since HandleInput shows an error in case the information in the input equals “redberry.” To fix it, you should change the username. Unfortunately, some errors are impossible to catch:
Error boundaries do not catch errors for:
- Event handlers (learn more)
- Asynchronous code (e.g., setTimeout or requestAnimationFrame callbacks)
- Server-side rendering
- Errors are thrown in the error boundary itself (rather than its children)
For further information, visit the Github page.
Conclusion
React Error Boundaries are a crucial tool for handling unexpected errors that occur within a React component and preventing them from propagating to the parent components, potentially causing a complete UI breakdown. With the help of Error Boundaries, developers can implement customized error-handling strategies and display a fallback UI to the user, preserving the user experience.
It is important to note that Error Boundaries only catch errors within the render() method or in the component’s lifecycle methods. They will not catch errors in asynchronous code such as setTimeout() or network requests. Additionally, having at least one global Error Boundary component in your application is recommended to handle errors that may occur outside of a specific component. Finally, Error Boundaries should only be used for debugging and fallback UI purposes and not as a means of catching and fixing errors in production.
Written by: Nikoloz Palagashvili