diff --git a/hooks/src/index.js b/hooks/src/index.js index 5a8c4fdeb9..b40dee3d2b 100644 --- a/hooks/src/index.js +++ b/hooks/src/index.js @@ -268,18 +268,17 @@ export function useErrorBoundary(cb) { const state = getHookState(currentIndex++, 10); const errState = useState(); state._value = cb; - if (!currentComponent.componentDidCatch) { + if (!state._reset) { + const previousDidCatch = currentComponent.componentDidCatch; currentComponent.componentDidCatch = err => { if (state._value) state._value(err); + if (previousDidCatch) previousDidCatch.call(currentComponent, err); errState[1](err); }; + state._reset = () => errState[1](); } - return [ - errState[0], - () => { - errState[1](undefined); - } - ]; + + return [errState[0], state._reset]; } /** diff --git a/hooks/test/browser/errorBoundary.test.js b/hooks/test/browser/errorBoundary.test.js index 4c2f7f3927..e84669bf4f 100644 --- a/hooks/test/browser/errorBoundary.test.js +++ b/hooks/test/browser/errorBoundary.test.js @@ -89,4 +89,62 @@ describe('errorBoundary', () => { expect(spy2).to.be.calledOnce; expect(spy2).to.be.calledWith(error); }); + + it('ensures referential stability of `reset`', () => { + const resets = []; + + const Test = () => { + const [, reset] = useErrorBoundary(); + + resets.push(reset); + return null; + }; + + render(, scratch); + rerender(); + render(, scratch); + rerender(); + + expect(resets[0]).to.equal(resets[1]); + }); + + it('can be called multiple times in a single component, idependently', () => { + const spy = sinon.spy(), + spy2 = sinon.spy(); + let resetErr; + const error = new Error('test'); + const Throws = () => { + throw error; + }; + + const App = props => { + const [err, reset] = useErrorBoundary(props.onError); + const [err2] = useErrorBoundary(props.onError2); + resetErr = reset; + + return ( +
+ {err &&

Error

} + {err2 &&

Error2

} + {err || err2 ? null : } +
+ ); + }; + + render(, scratch); + rerender(); + expect(scratch.innerHTML).to.equal('

Error

Error2

'); + expect(spy).to.be.calledOnce; + expect(spy).to.be.calledWith(error); + expect(spy2).to.be.calledOnce; + expect(spy2).to.be.calledWith(error); + + resetErr(); + rerender(); + expect(scratch.innerHTML).to.equal('

Error2

'); + expect(spy).to.be.calledOnce; + expect(spy).to.be.calledWith(error); + expect(spy2).to.be.calledOnce; + expect(spy2).to.be.calledWith(error); + }); });