Lin Du
Lin Du

Reputation: 102247

How to catch the error thrown inside event handler of a child component?

There are two functional components. An unknown and unexpected error is thrown out in the click event handler doStuff. unexpected means I can't use the try...catch... statement as usual.

I have read Error Boundaries, but it seems to only work for the class-based components.

How can I catch the error and recover from this situation?

Additional context: I can't modify the code of the Child component.

import React from 'react';

function Child() {
  function doStuff() {
    throw new Error('An unknown and unexpected error from somewhere');
  }

  return <p onClick={doStuff}>my-button</p>;
}

function Parent() {
  // How to catch the click event handler error in parent
  return <Child></Child>;
}

Upvotes: 3

Views: 1641

Answers (2)

invisal
invisal

Reputation: 11171

I think you can create the error boundary hook like this

const CatchErrorContext = React.createContext({});

function withCatchError(Component) {
  return class extends React.Component {
    componentDidCatch(error, errorInfo) {
      this.setState({ error, errorInfo });
    }

    render() {
      return (
        <CatchErrorContext.Provider value={{ ...this.state }}>
          <Component {...this.props} />
        </CatchErrorContext.Provider>
      );
    }
  };
}

function useCatchError() {
  return useContext(CatchErrorContext);
}

Then, you can use

const Parent = withCatchError(() => {
  const { error } = useCatchError();

  if (error) return <div>Some error</div>
  return ...
})

The error boundary cannot catch every error. It can only catch the rendering error. If you want to catch all the error, you can do something like this

function useCatchGlobalError() {
  const [error, setError] = useState(null);

  useEffect(() => {
    const errorHandler = (e) => {
      setError(e);
      return true;
    };

    window.addEventListener("error", errorHandler);
    return () => window.removeEventListener("error", errorHandler);
  }, []);

  return error;
}

Then you can use

function Parent() {
  const error = useCatchGlobalError();
  ...
}

Upvotes: 3

Ren&#233; Link
Ren&#233; Link

Reputation: 51353

I'm not sure what you mean with a I can't use the try...catch... statement as usual. I would use a delegate function that can catch the error.

function Child() {
  function doStuff() {
    throw new Error('An unknown and unexpected error from somewhere');
  }

  function tryDoStuff(){
    try {
      doStuff()
    } catch (e) {
      console.log("Caught: " + e);
    }
  }
  
  return <button onClick={tryDoStuff} className="btn btn-primary">my-button</button>;
}

function Parent() {
  // How to catch the click event handler error in parent
  return <Child></Child>;
}

ReactDOM.render(<Parent/>, document.getElementById("root"));
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 1

Related Questions