diff --git a/packages/react-devtools-shared/src/__tests__/profilingCache-test.js b/packages/react-devtools-shared/src/__tests__/profilingCache-test.js
index 00605d857a153..ae01abaf3c2a2 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 setBarUnmounted;
+ function Bar() {
+ const [, setState] = React.useState(false);
+
+ React.useLayoutEffect(() => {
+ return () => setState(true);
+ });
+
+ return null;
+ }
+
+ function App() {
+ const [barUnmounted, _setBarUnmounted] = React.useState(false);
+ setBarUnmounted = _setBarUnmounted;
+ return <>{!barUnmounted && }>;
+ }
+
+ const root = ReactDOM.createRoot(document.createElement('div'));
+ utils.act(() => root.render());
+ utils.act(() => store.profilerStore.startProfiling());
+ utils.act(() => setBarUnmounted(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', () => {
+ function Foo() {
+ return ;
+ }
+
+ let setBarUnmounted;
+ function Bar() {
+ const [, setState] = React.useState(false);
+
+ React.useLayoutEffect(() => {
+ return () => setState(true);
+ });
+
+ return null;
+ }
+
+ function App() {
+ const [barUnmounted, _setBarUnmounted] = React.useState(false);
+ setBarUnmounted = _setBarUnmounted;
+ return <>{!barUnmounted && }>;
+ }
+
+ const root = ReactDOM.createRoot(document.createElement('div'));
+ utils.act(() => root.render());
+ utils.act(() => store.profilerStore.startProfiling());
+ utils.act(() => setBarUnmounted(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 setBarUnmounted;
+ function Bar() {
+ const [, setState] = React.useState(false);
+ React.useEffect(() => {
+ return () => setState(true);
+ });
+
+ return null;
+ }
+
+ function App() {
+ const [barUnmounted, _setBarUnmounted] = React.useState(false);
+ setBarUnmounted = _setBarUnmounted;
+ return <>{!barUnmounted && }>;
+ }
+
+ const root = ReactDOM.createRoot(document.createElement('div'));
+ utils.act(() => root.render());
+ utils.act(() => store.profilerStore.startProfiling());
+ utils.act(() => setBarUnmounted(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;
}