Reputation: 1106
Apologies, I know this has been asked, but I can't find a single example that works for me. Seems like it's not easy enough to understand if so many are struggling with it.
I just need to know how to catch errors in React in a clean, simple way. I'm using CRA 2.0/React 16.7. I want a try/catch block at the action level, as this is where business logic is concentrated in my app.
I read this and implemented it as described, but my ErrorBoundary object never catches errors.
Example:
import React, { Component } from "react";
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
console.log("ErrorBoundary: ", error);
console.log("ErrorBoundary: ", info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
I'm wrapping my routes at the top level:
return (
<BrowserRouter>
<ErrorBoundary>
<div className="main-container">
<SideBar />
<div className="some-class">
<Switch>
<Route path="/events" component={Events} />
<Route exact path="/" component={Index} />
</Switch>
</div>
</div>
</ErrorBoundary>
</BrowserRouter>
);
In "events" above, I make an API call which is throwing a 500 error on the API side:
componentDidMount() {
this.props.getAllEvents();
}
Here's the Redux action:
export const getAllEvents = () => {
return async (dispatch, getState) => {
try {
let state = getState();
const result = await get({
state: state,
route: "/v1/events"
});
dispatch({
type: GET_EVENTS,
payload: result
});
} catch (e) {
console.log("Something went wrong at action...");
}
};
};
..."get()" is just wrapping an Axios GET - nothing fancy.
I see a 500 error in the console from the failed API call. I never see the "Something went wrong..." in the console, from the catch block above, though that line does get hit while debugging.
The "componentDidCatch()" method never gets called - "hasError" is always false and it always renders the children.
If I remove the throw block in the API endpoint, everything works fine and I get my data. I'm just not able to catch errors at the UI level. I've tried a try/catch in the "componentDidMount()", I've tried removing the try/catch block in the action...behavior doesn't change.
Thanks in advance.
Upvotes: 7
Views: 7028
Reputation: 5742
As the other answer correctly stated, React error boundary will only catch rendering errors.
Since you are using redux, you could build your own mechanism for cases like yours:
error
property.error
property. It receives the action and update a "global error" state in your state treeconnect
ed wrapper component sees this change in state and displays fallback UI.For example:
Dispatch an action with error
property:
export const getAllEvents = () => {
return async (dispatch, getState) => {
try {
...
} catch (e) {
dispatch({
type: ERROR,
error: e // dispatch an action that has `error` property
});
}
};
};
A reducer sees it and update the state.error
part:
export default function errorReducer(state = null, action) {
const { type, error } = action;
if (type === RESET_ERROR_MESSAGE) { // an action to clear the error
return null;
} else if (error) { // any type of action, but contains an `error`
return error;
}
return state;
}
Now you wrap your app in a connected
boundary:
function Wrapper({error}) {
if(error) return <h1>Something went wrong</h1>;
return <App/>;
}
export default connect(
state => ({
error: state.error,
})
)(Wrapper);
Upvotes: 6
Reputation: 3671
From the React Error Boundaries docs:
Error boundaries do not catch errors for:
- Event handlers (learn more)
- Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
- Server side rendering
- Errors thrown in the error boundary itself (rather than its children)
Upvotes: 1