React 16 introduced the concept of an error boundary. Official docs about error boundaries
Any React component becomes an error boundary by defining a componentDidCatch(error, info) method. componentDidCatch() will be called if any of the component’s children throw an error. It is the declarative equivalent of a try catch in imperative code. I’m a big fan, it enables very neat error handling in React.
Recently I wrote an error boundary component which makes it possible for the user to re-attempt a failed operation. While writing a test for this retry behaviour I ran into an interesting (to me) scope problem.
Below is a cut down version of the test.
The error boundary component contains an error generator component. The error generator has a shouldError prop. If that is true then it throws an exception. To test the human initiated retry we need an error to occur, retry is activated then the error doesn’t reoccur.
let shouldError = true const TestComponent: React.SFC = () => ( <ErrorBoundary onError={() => { shouldError = false return (<div>error has occurred</div>) }} log={log} > no error <ErrorGenerator shouldError={shouldError} /> </ErrorBoundary> ) const wrapper = mount(<TestComponent />) // error expect(wrapper.text()).toContain('error has occurred') // human initiated retry redacted for simplicity // success expect(wrapper.text()).toContain('no error')
Here’s the problem. The value of the the shouldError prop never changes so ErrorGenerator always generates an error. The variable being passed in to the prop changes but the prop assignment is never re-evaluated.
It took a lot of staring at it to realize that this
<ErrorGenerator shouldError={shouldError} />
needs to be changed to this as the assignment of a local variable to the prop of the test component is only evaluated once.
<ErrorGenerator shouldError={() => shouldError} />