How to handle multiple errors in React?

Viewed 1094

In my React app I have created the below NoteDetail component. My question is: What is the best practice to handle multiple error variables, in this case one that I might get from my GET API-request and another from my DELETE API-request? The first solution that comes into my head is to have dedicated getError and deleteError variables, but I don't know if this is the recommended way to handle this.

import { Link, useParams } from "react-router-dom";
import useFetch from "./useFetch";
import { api } from "./api";
import axios from "axios";

const NoteDetail = () => {
  const { id } = useParams();
  const { data: note, error, loading } = useFetch(api.notes.retrieve(id));
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);

  const handleClick = () => {
    axios
      .delete(api.notes.delete(id))
      .then((response) => {
        setLoading(false);
        history.pushState("/");
      })
      .catch((error) => {
        setLoading(false);
        setError(error.message || error);
      });
  };

  return (
    <div className="note-details">
      {loading && <div>Loading...</div>}
      {error && <div>{error}</div>}
      {note && (
        <article>
          <h2>{note.title}</h2>
          <p>Written by {note.author}</p>
          <div>{note.body}</div>
          <Link to={"/update/" + note.id}>
            <button>Edit</button>
          </Link>
          <button onClick={handleClick}>Delete</button>
        </article>
      )}
    </div>
  );
};

export default NoteDetail;
2 Answers

The best way, according to the doc, would be Error Boundaries concept.

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.

A class component becomes an error boundary if it defines a new lifecycle method called componentDidCatch(error, info):

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

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

Then you can wrap your other components by this ErrorBoundary component as below:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

Just remember that error boundaries only catch errors in the components below them in the tree.

LIVE DEMO

I am curious what happens when you have multiple error boundaries

function renderWithError() {
  throw new Error('error');
}

function A() {
  return <ErrorBoundary name="boundary-2">{renderWithError()}</ErrorBoundary>;
}

function App() {
  return (
    <ErrorBoundary name="boundary-1">
      <A />
    </ErrorBoundary>
  )
}
Related