32
loading...
This website collects cookies to deliver better user experience
try
and catch
blocks of JSX ecosystem.const App = () => {
return (
<div>
<h1>Counter Example</h1>
<ErrorBoundary fallBackUIComponent={<FallBackUI />}>
<BuggyComponent />
</ErrorBoundary>
</div>
);
}
ErrorBoundary
component is placed as a parent to a component which we suspect might cause an error.BuggyComponent
the nearest error boundary which is ErrorBoundary
component catches it and displays a fallback UI. Below Gif will explain this scenario.ErrorBoundary
:
Before Implementing the error boundary we should keep in mind the following things:
class
based component.static getDerivedStateFromError()
: A static method which is executed before the DOM is ready(during the rendering phase of the component). This will get invoked whenever descendant component throws an error.componentDidCatch()
: This will get invoked whenever a descendant component throws an error. This component is called during commit
phase i.e. When the DOM is ready. It can be used to perform side-effects in the component. It receives two parameters:
error
- error that is being thrown.info
- An object with componentStack which tells us which component threw an error.Now we can move towards the implementation of the error boundary. Below code will demonstrate a class based react error boundary:
class ErrorBoundary extends React.Component {
constructor(props){
super(props);
this.state = {
hasError: false
};
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
//Can be used to log to any logging service like sentry
console.log("Catched error", errorInfo);
}
render(){
if(this.state.hasError){
return(
// <h3>Something went wrong!</h3>
//Can be a static or a fall-back component passed as a prop.
this.props.fallBackUIComponent
);
}
return this.props.children;
}
}
getDerivedStateFromError
returns a value to update the state of the component in this case hasError
is set to true.componentDidCatch
will also catch the error along with the stack trace of the error. This will occur on the commit phase of the component.render
function if the hasError
state is true
then this will print our fallback component which we passed it as a prop. Else it will return the children
.ErrorBoundary
Component so that it catches the error thrown by it's descendant. Below example will give you a clear idea of it's usage://Component for fallback UI:
const FallBackUI = () => {
return (
<>
<h3>Something went wrong</h3>
</>
);
}
const BuggyComponent = () => {
const [count, setCount] = React.useState(0);
const increaseCounter = () => {
setCount(preVal => preVal + 1);
}
if(count === 5) {
throw new Error("Crashing the app!!");
}
return (
<>
<div className="counter--block">
<span>Counter</span>
<span>{count}</span>
</div>
<button onClick={increaseCounter}>Increase count</button>
</>
);
}
const App = () => {
return (
<div>
<h1>Counter Example</h1>
<ErrorBoundary fallBackUIComponent={<FallBackUI />}>
<BuggyComponent />
</ErrorBoundary>
</div>
);
}
ReactDOM.render(
<App />
,
document.getElementById("root")
);
Below mentioned errors are not being catched by react's error boundaries:
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)
error boundary
not to work.ErrorBoundary
component so that it catches error. Below example will provide clear understanding:5
:const BuggyComponent = () => {
const [count, setCount] = React.useState(0);
const increaseCounter = () => {
setCount(preVal => preVal + 1);
}
if(count === 5) {
throw new Error("Crashing the app!!");
}
return (
<>
<div className="counter--block">
<span>Counter</span>
<span>{count}</span>
</div>
<button onClick={increaseCounter}>Increase count</button>
</>
);
}
ErrorBoundary
Component to catch error, since the BuggyComponent
is not being wrapped with ErrorBoundary
but rather the content of this component is wrapped with ErrorBoundary
.return (
<ErrorBoundary>
<div className="counter--block">
<span>Counter</span>
<span>{count}</span>
</div>
<button onClick={increaseCounter}>Increase count</button>
</ErrorBoundary>
);
BuggyComponent
. To make this work we can do something like this:const App = () => {
return (
<div>
<h1>Counter Example</h1>
<ErrorBoundary>
<BuggyComponent />
</ErrorBoundary>
</div>
);
}
ErrorBoundary
will catch the error thrown by the BuggyComponent
since it is being wrapped by the error boundary.if
block for this is placed in the rendering phase of the component because of which it creates a valid case for ErrorBoundary
to catch the error.const BuggyComponent = () => {
const [count, setCount] = React.useState(0);
const increaseCounter = () => {
setCount(preVal => preVal + 1);
}
if(count === 5) {
throw new Error("Crashing the app!!");
}
return (
<>
<div className="counter--block">
<span>Counter</span>
<span>{count}</span>
</div>
<button onClick={increaseCounter}>Increase count</button>
</>
);
}
const App = () => {
return (
<div>
<h1>Counter Example</h1>
<ErrorBoundary>
<BuggyComponent />
</ErrorBoundary>
</div>
);
}
if
block inside the increaseCounter
function. The above example is altered to showcase this scenario:const BuggyComponent = () => {
const [count, setCount] = React.useState(0);
const increaseCounter = () => {
setCount(preVal => preVal + 1);
if(count === 5) {
throw new Error("Crashing the app!!");
}
}
return (
<>
<div className="counter--block">
<span>Counter</span>
<span>{count}</span>
</div>
<button onClick={increaseCounter}>Increase count</button>
</>
);
}
const App = () => {
return (
<div>
<h1>Counter Example</h1>
<ErrorBoundary>
<BuggyComponent />
</ErrorBoundary>
</div>
);
}
react-error-boundary
is a pretty impressive package. It solves most of the challenges faced by react's error boundary where it won't be able to catch errors such as errors thrown from event handlers, asynchornous code etc.react-error-boundary
:import {ErrorBoundary} from 'react-error-boundary';
function ErrorFallback({error}) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre style={{color: 'red'}}>{error.message}</pre>
</div>
)
}
const BuggyCounter = () => {
const [count, setCount] = React.useState(0);
const handleIncrement = () => {
setCount(preVal => preVal + 1);
}
if(count === 5){
throw new Error("New Crashing Seq. Initiated");
}
return(
<div className="counter--block">
<span>Count</span>
<span>{count}</span>
<button onClick={handleIncrement}>Increment count</button>
</div>
);
}
const App = () => {
return(
<>
<h1>Counter Example</h1>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<BuggyCounter />
</ErrorBoundary>
</>
)
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
import React from "react";
import ReactDOM from "react-dom";
import * as Sentry from "@sentry/react";
import App from "./App";
Sentry.init({ dsn: "https://[email protected]/0" });
const BuggyCounter = () => {
const [counter, setCounter] = useState(0);
return (
<>
<div className="counter--value">
{counter}
</div>
<div>
<button
className="counter--button"
onClick={() => { throw new Error("New Test Error")}}>
increment count
</button>
</div>
</>
)
}
const App = () => {
return (
<Sentry.ErrorBoundary fallback={"An error has occurred"}>
<BuggyCounter />
</Sentry.ErrorBoundary>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
// Can also use with React Concurrent Mode
// ReactDOM.createRoot(document.getElementById('root')).render(<App />);
Sentry.init({ dsn: "https://[email protected]/0" });
dsn
is data source name which tells the SDK where to send the events.import * as Sentry from "@sentry/react";
const App = () => {
return (
<Sentry.ErrorBoundary fallback={"An error has occurred"}>
<BuggyCounter />
</Sentry.ErrorBoundary>
);
}
Implementation of react error boundary from scratch:
https://codepen.io/keyurparalkar/pen/LYWJKvm?editors=0010
Implementation of react error boundary using react-error-boundary
package:
https://codepen.io/keyurparalkar/pen/bGqQNJe