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 daa4d4e1030c3..0f7b15cd019b9 100644
--- a/packages/react-devtools-shared/src/backend/renderer.js
+++ b/packages/react-devtools-shared/src/backend/renderer.js
@@ -2586,7 +2586,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;
}