From 769dd522a2f6843f7e22d903fce296cb9faab88b Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 14 Nov 2019 18:40:25 +0000 Subject: [PATCH] [Fast Refresh] Fix for intentional unmounts after an error (#17368) --- .../react-refresh/src/ReactFreshRuntime.js | 4 +++ .../src/__tests__/ReactFresh-test.js | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/packages/react-refresh/src/ReactFreshRuntime.js b/packages/react-refresh/src/ReactFreshRuntime.js index 73e4747fbf..1b40d1fa6d 100644 --- a/packages/react-refresh/src/ReactFreshRuntime.js +++ b/packages/react-refresh/src/ReactFreshRuntime.js @@ -468,6 +468,10 @@ export function injectIntoGlobalHook(globalObject: any): void { // TODO: Maybe we could fix this as the same time as when we fix // DevTools to not depend on `alternate.memoizedState.element`. didSomeRootFailOnMount = true; + } else if (!didError && failedRoots.has(root)) { + // The error is fixed but the component is still unmounted. + // This means that the unmount was not caused by a failed refresh. + failedRoots.delete(root); } } } else { diff --git a/packages/react-refresh/src/__tests__/ReactFresh-test.js b/packages/react-refresh/src/__tests__/ReactFresh-test.js index 84d8f60cc0..ae3e598a1b 100644 --- a/packages/react-refresh/src/__tests__/ReactFresh-test.js +++ b/packages/react-refresh/src/__tests__/ReactFresh-test.js @@ -2870,6 +2870,42 @@ describe('ReactFresh', () => { }); expect(container.innerHTML).toBe(''); expect(ReactFreshRuntime.hasUnrecoverableErrors()).toBe(false); + + // Mount a new container. + render(() => { + function Hello() { + return

Hi

; + } + $RefreshReg$(Hello, 'Hello'); + + return Hello; + }); + expect(container.innerHTML).toBe('

Hi

'); + expect(ReactFreshRuntime.hasUnrecoverableErrors()).toBe(false); + + // Break again. + expect(() => { + patch(() => { + function Hello() { + throw new Error('Oops'); + } + $RefreshReg$(Hello, 'Hello'); + }); + }).toThrow('Oops'); + expect(container.innerHTML).toBe(''); + expect(ReactFreshRuntime.hasUnrecoverableErrors()).toBe(false); + + // Check we don't attempt to reverse an intentional unmount, even after an error. + ReactDOM.unmountComponentAtNode(container); + expect(container.innerHTML).toBe(''); + patch(() => { + function Hello() { + return

Never mind me!

; + } + $RefreshReg$(Hello, 'Hello'); + }); + expect(container.innerHTML).toBe(''); + expect(ReactFreshRuntime.hasUnrecoverableErrors()).toBe(false); } });