DEV Community

Lautaro Suarez
Lautaro Suarez

Posted on

Handling errors in React

Errors when developing are something that you will always have and it's better to know that i know how to handle them. In this post I'm going to write about how to handle them in React.

Try/Catch

I will start talking about try/catch method which is really simple but useful at the same time.
We just can use it for imperative code and event handlers which are often used in a declarative way in react.

Event handler using Try/Catch

The provided code demonstrates a basic event handler that employs a try/catch block. Within the try block, there is a comment indicating the potential presence of code performing operations like making API calls using fetch or internal API functions. The try block attempts to execute these operations, while the catch block is responsible for capturing any encountered errors in case the operations within the try block fail.

const MyComponent = () => {
  const [error, setError] = useState(null)
  handleClick() {
    try {
      // Do something that could throw
    } catch (error) {
     setError(error);
    }
  }

    if (error) {
      return <h1>Caught an error.</h1>
    }
    return <button onClick={this.handleClick}>Click Me</button>
}
Enter fullscreen mode Exit fullscreen mode

Common mistakes

When utilizing the try/catch method, it is essential to be mindful of certain considerations. As this method operates in an imperative manner, it can lead to complications when employed alongside React lifecycle methods like useEffect, children components, and state updates during rendering.

Example with useEffect (Wrong)
This will never work, since we can't wrap useEffect with try/catch.

try{
 useEffect(() => {
  throw new Error("Main Error!")
},[])
}catch(e){
 // useEffect throws, but this will never be called
}
Enter fullscreen mode Exit fullscreen mode

Example with useEffect (Right)
This is an example of how to use try/catch with useEffect the right way.

useEffect(() => {
 try{
  throw new Error("Main Error!") 
}catch(e){
 // this one will be caught
}
}, [])
Enter fullscreen mode Exit fullscreen mode

Example with children components
We also can not do this cause it will be useless cause it will never catch an error since the child component belongs to the lyfecycle method of the react virutal DOM.

const Component = () => {
  try {
    return <Child />
  } catch(e) {
    // still useless for catching errors inside Child component, won't be triggered
  }
}
Enter fullscreen mode Exit fullscreen mode

Example setting state during render
The following example will just cause an infinite loop in case of an error.

const Component = () => {
 const [error, setError] = useState(false);

 try{
  something();
}catch(e){
 setError(e)
 }
}
Enter fullscreen mode Exit fullscreen mode

We could use something like this though, where we will set the state inside a useEffect but we will also have a try/catch at the render but in case of error it will return an error screen instead of setting some state.

const Component = () => {
 const [error, setError] = useState(false);

 useEffect(() => {
 try{
 //something
}catch(e){
 setError(true);
}
},[])

 try{
  something();
}catch(e){
 return <ErrorScreen/>
 }

if(error) return <ErrorScreen/>

return <NiceComponent/>
}
Enter fullscreen mode Exit fullscreen mode

React ErrorBoundary component

To be able to avoid the limitations of try/catch we could use a "Error boundaries" Api that react give us that converts a regular component into a try/catch, only for React declarative code.

How to implement it?
Here is the way react documentation show us how to implement it.

const Component = () => {
 return(
   <ErrorBoundary>
    <ChildComponent/>
   </ErrorBoundary>
)
}
Enter fullscreen mode Exit fullscreen mode

If something goes wrong in the child component or their children during render, the error will be caught and dealt with.

The catch is that react docs does not give us the component already built, but it gives us a tool to implement it.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    // initialize the error state
    this.state = { hasError: false };
  }

  // if an error happened, set the state to true
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    // if error happened, return a fallback component
    if (this.state.hasError) {
      return <>Oh no! Epic fail!</>
    }

    return this.props.children;
  }
}
Enter fullscreen mode Exit fullscreen mode

And no, react does not support error boundaries built as a functional component yet.
So let me break down the code for you and explain it a little but is not that hard to understand if you have some experience with react.
Basically we extend react component, then we initialise a constructor that will have a hasError as the state, getDerivatedStateFromError was literally mainly build to be able to catch errors on the lyfecycle of react, so if it finds an error it will change the value of hasError to true and if hasError is true our errorBoundary will return "Oh no! Epic fail!" but in case it does not have an error it will just return the children the component has wrap it.

Add a fallback to the errorBoundary

Let's say we want to add a specific fallback depending what components had an error we could add a fallback to the errorBoundary so we will be able to return that specific component.

ErrorBoundary

render() {
  // if error happened, return a fallback component
  if (this.state.hasError) {
    return this.props.fallback;
  }

  return this.props.children;
}
Enter fullscreen mode Exit fullscreen mode

And this is how we would use it

const Component = () => {
  return (
    <ErrorBoundary fallback={<>Oh no! Do something!</>}>
      <SomeChildComponent />
    </ErrorBoundary>
  )
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

We always would need to use try/catch to catch async functions and imperative code but the best would be to use a combination of both, Error Boundaries and try/catch to have a well documented code that handles errors right.

Hope someone found it useFul, and if you have any questions i would try to solve them, catch(e){Or not}.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.