From 6cfb14805733a46035104b139ce84bbc9b80d493 Mon Sep 17 00:00:00 2001 From: Luna Date: Thu, 20 Jan 2022 15:41:32 -0500 Subject: [PATCH] Filter out deleted components that are added to the updaters list --- .../src/__tests__/profilingCache-test.js | 93 +++++++++++++++++++ .../src/backend/renderer.js | 4 +- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/__tests__/profilingCache-test.js b/packages/react-devtools-shared/src/__tests__/profilingCache-test.js index 00605d857a153..67db757461a6d 100644 --- a/packages/react-devtools-shared/src/__tests__/profilingCache-test.js +++ b/packages/react-devtools-shared/src/__tests__/profilingCache-test.js @@ -752,4 +752,97 @@ describe('ProfilingCache', () => { utils.act(() => store.profilerStore.stopProfiling()); expect(container.textContent).toBe('About'); }); + + it('components that were deleted and added to updaters during the layout phase should not crash', () => { + let setChildUnmounted; + function Child() { + const [, setState] = React.useState(false); + + React.useLayoutEffect(() => { + return () => setState(true); + }); + + return null; + } + + function App() { + const [childUnmounted, _setChildUnmounted] = React.useState(false); + setChildUnmounted = _setChildUnmounted; + return <>{!childUnmounted && }; + } + + const root = ReactDOM.createRoot(document.createElement('div')); + utils.act(() => root.render()); + utils.act(() => store.profilerStore.startProfiling()); + utils.act(() => setChildUnmounted(true)); + utils.act(() => store.profilerStore.stopProfiling()); + + const updaters = store.profilerStore.getCommitData(store.roots[0], 0) + .updaters; + expect(updaters.length).toEqual(1); + expect(updaters[0].displayName).toEqual('App'); + }); + + it('components in a deleted subtree and added to updaters during the layout phase should not crash', () => { + let setChildUnmounted; + function Child() { + return ; + } + + function GrandChild() { + const [, setState] = React.useState(false); + + React.useLayoutEffect(() => { + return () => setState(true); + }); + + return null; + } + + function App() { + const [childUnmounted, _setChildUnmounted] = React.useState(false); + setChildUnmounted = _setChildUnmounted; + return <>{!childUnmounted && }; + } + + const root = ReactDOM.createRoot(document.createElement('div')); + utils.act(() => root.render()); + utils.act(() => store.profilerStore.startProfiling()); + utils.act(() => setChildUnmounted(true)); + utils.act(() => store.profilerStore.stopProfiling()); + + const updaters = store.profilerStore.getCommitData(store.roots[0], 0) + .updaters; + expect(updaters.length).toEqual(1); + expect(updaters[0].displayName).toEqual('App'); + }); + + it('components that were deleted should not be added to updaters during the passive phase', () => { + let setChildUnmounted; + function Child() { + const [, setState] = React.useState(false); + React.useEffect(() => { + return () => setState(true); + }); + + return null; + } + + function App() { + const [childUnmounted, _setChildUnmounted] = React.useState(false); + setChildUnmounted = _setChildUnmounted; + return <>{!childUnmounted && }; + } + + const root = ReactDOM.createRoot(document.createElement('div')); + utils.act(() => root.render()); + utils.act(() => store.profilerStore.startProfiling()); + utils.act(() => setChildUnmounted(true)); + utils.act(() => store.profilerStore.stopProfiling()); + + const updaters = store.profilerStore.getCommitData(store.roots[0], 0) + .updaters; + expect(updaters.length).toEqual(1); + expect(updaters[0].displayName).toEqual('App'); + }); }); diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 631521791a549..3ef4250aaceab 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -2573,7 +2573,9 @@ export function attach( function getUpdatersList(root): Array | null { return root.memoizedUpdaters != null - ? Array.from(root.memoizedUpdaters).map(fiberToSerializedElement) + ? Array.from(root.memoizedUpdaters) + .filter(fiber => getFiberIDUnsafe(fiber) !== null) + .map(fiberToSerializedElement) : null; }