Call cleanup of insertion effects when hidden (#30954)

Insertion effects do not unmount when a subtree is removed while
offscreen.

Current behavior for an insertion effect is if the component goes

- *visible -> removed:* calls insertion effect cleanup
- *visible -> offscreen -> removed:* insertion effect cleanup is never
called

This makes it so we always call insertion effect cleanup when removing
the component.

Likely also fixes https://github.com/facebook/react/issues/26670

---------

Co-authored-by: Rick Hanlon <rickhanlonii@fb.com>
This commit is contained in:
Jan Kassens
2024-09-13 13:18:14 -07:00
committed by GitHub
parent 633a0fe536
commit d3d4d3a46b
13 changed files with 366 additions and 5 deletions
+74 -1
View File
@@ -40,6 +40,7 @@ import type {
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableHiddenSubtreeInsertionEffectCleanup,
enablePersistedModeClonedFlag,
enableProfilerTimer,
enableProfilerCommitHooks,
@@ -147,6 +148,7 @@ import {
getExecutionContext,
CommitContext,
NoContext,
setIsRunningInsertionEffect,
} from './ReactFiberWorkLoop';
import {
NoFlags as NoHookEffect,
@@ -1324,7 +1326,78 @@ function commitDeletionEffectsOnFiber(
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
if (!offscreenSubtreeWasHidden) {
if (enableHiddenSubtreeInsertionEffectCleanup) {
// When deleting a fiber, we may need to destroy insertion or layout effects.
// Insertion effects are not destroyed on hidden, only when destroyed, so now
// we need to destroy them. Layout effects are destroyed when hidden, so
// we only need to destroy them if the tree is visible.
const updateQueue: FunctionComponentUpdateQueue | null =
(deletedFiber.updateQueue: any);
if (updateQueue !== null) {
const lastEffect = updateQueue.lastEffect;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
const tag = effect.tag;
const inst = effect.inst;
const destroy = inst.destroy;
if (destroy !== undefined) {
if ((tag & HookInsertion) !== NoHookEffect) {
// TODO: add insertion effect marks and profiling.
if (__DEV__) {
setIsRunningInsertionEffect(true);
}
inst.destroy = undefined;
safelyCallDestroy(
deletedFiber,
nearestMountedAncestor,
destroy,
);
if (__DEV__) {
setIsRunningInsertionEffect(false);
}
} else if (
!offscreenSubtreeWasHidden &&
(tag & HookLayout) !== NoHookEffect
) {
// Offscreen fibers already unmounted their layout effects.
// We only need to destroy layout effects for visible trees.
if (enableSchedulingProfiler) {
markComponentLayoutEffectUnmountStarted(deletedFiber);
}
if (shouldProfile(deletedFiber)) {
startLayoutEffectTimer();
inst.destroy = undefined;
safelyCallDestroy(
deletedFiber,
nearestMountedAncestor,
destroy,
);
recordLayoutEffectDuration(deletedFiber);
} else {
inst.destroy = undefined;
safelyCallDestroy(
deletedFiber,
nearestMountedAncestor,
destroy,
);
}
if (enableSchedulingProfiler) {
markComponentLayoutEffectUnmountStopped();
}
}
}
effect = effect.next;
} while (effect !== firstEffect);
}
}
} else if (!offscreenSubtreeWasHidden) {
const updateQueue: FunctionComponentUpdateQueue | null =
(deletedFiber.updateQueue: any);
if (updateQueue !== null) {
@@ -7,6 +7,7 @@ let Activity;
let useState;
let useLayoutEffect;
let useEffect;
let useInsertionEffect;
let useMemo;
let useRef;
let startTransition;
@@ -25,6 +26,7 @@ describe('Activity', () => {
LegacyHidden = React.unstable_LegacyHidden;
Activity = React.unstable_Activity;
useState = React.useState;
useInsertionEffect = React.useInsertionEffect;
useLayoutEffect = React.useLayoutEffect;
useEffect = React.useEffect;
useMemo = React.useMemo;
@@ -43,6 +45,13 @@ describe('Activity', () => {
}
function LoggedText({text, children}) {
useInsertionEffect(() => {
Scheduler.log(`mount insertion ${text}`);
return () => {
Scheduler.log(`unmount insertion ${text}`);
};
});
useEffect(() => {
Scheduler.log(`mount ${text}`);
return () => {
@@ -1436,6 +1445,63 @@ describe('Activity', () => {
);
});
// @gate enableActivity
it('insertion effects are not disconnected when the visibility changes', async () => {
function Child({step}) {
useInsertionEffect(() => {
Scheduler.log(`Commit mount [${step}]`);
return () => {
Scheduler.log(`Commit unmount [${step}]`);
};
}, [step]);
return <Text text={step} />;
}
function App({show, step}) {
return (
<Activity mode={show ? 'visible' : 'hidden'}>
{useMemo(
() => (
<Child step={step} />
),
[step],
)}
</Activity>
);
}
const root = ReactNoop.createRoot();
await act(() => {
root.render(<App show={true} step={1} />);
});
assertLog([1, 'Commit mount [1]']);
expect(root).toMatchRenderedOutput(<span prop={1} />);
// Hide the tree. This will not unmount insertion effects.
await act(() => {
root.render(<App show={false} step={1} />);
});
assertLog([]);
expect(root).toMatchRenderedOutput(<span hidden={true} prop={1} />);
// Update.
await act(() => {
root.render(<App show={false} step={2} />);
});
// The update is pre-rendered so insertion effects are fired
assertLog([2, 'Commit unmount [1]', 'Commit mount [2]']);
expect(root).toMatchRenderedOutput(<span hidden={true} prop={2} />);
// Reveal the tree.
await act(() => {
root.render(<App show={true} step={2} />);
});
// The update doesn't render because it was already pre-rendered, and the
// insertion effect already fired.
assertLog([]);
expect(root).toMatchRenderedOutput(<span prop={2} />);
});
describe('manual interactivity', () => {
// @gate enableActivity
it('should attach ref only for mode null', async () => {
@@ -1904,6 +1970,9 @@ describe('Activity', () => {
'outer',
'middle',
'inner',
'mount insertion inner',
'mount insertion middle',
'mount insertion outer',
'mount layout inner',
'mount layout middle',
'mount layout outer',
@@ -1964,6 +2033,22 @@ describe('Activity', () => {
});
assertLog(['unmount layout inner', 'unmount inner']);
await act(() => {
root.render(null);
});
assertLog([
'unmount insertion outer',
'unmount layout outer',
'unmount insertion middle',
'unmount layout middle',
...(gate('enableHiddenSubtreeInsertionEffectCleanup')
? ['unmount insertion inner']
: []),
'unmount outer',
'unmount middle',
]);
});
// @gate enableActivity
@@ -19,6 +19,7 @@ let resolveText;
let ReactNoop;
let Scheduler;
let Suspense;
let Activity;
let useState;
let useReducer;
let useEffect;
@@ -64,6 +65,7 @@ describe('ReactHooksWithNoopRenderer', () => {
useTransition = React.useTransition;
useDeferredValue = React.useDeferredValue;
Suspense = React.Suspense;
Activity = React.unstable_Activity;
ContinuousEventPriority =
require('react-reconciler/constants').ContinuousEventPriority;
if (gate(flags => flags.enableSuspenseList)) {
@@ -2997,6 +2999,57 @@ describe('ReactHooksWithNoopRenderer', () => {
root.render(<NotInsertion />);
});
});
// @gate enableActivity
it('warns when setState is called from offscreen deleted insertion effect cleanup', async () => {
function App(props) {
const [, setX] = useState(0);
useInsertionEffect(() => {
if (props.throw) {
throw Error('No');
}
return () => {
setX(1);
};
}, [props.throw, props.foo]);
return null;
}
const root = ReactNoop.createRoot();
await act(() => {
root.render(
<Activity mode="hidden">
<App foo="hello" />
</Activity>,
);
});
if (gate(flags => flags.enableHiddenSubtreeInsertionEffectCleanup)) {
await expect(async () => {
await act(() => {
root.render(<Activity mode="hidden" />);
});
}).toErrorDev(['useInsertionEffect must not schedule updates.']);
} else {
await expect(async () => {
await act(() => {
root.render(<Activity mode="hidden" />);
});
}).toErrorDev([]);
}
// Should not warn for regular effects after throw.
function NotInsertion() {
const [, setX] = useState(0);
useEffect(() => {
setX(1);
}, []);
return null;
}
await act(() => {
root.render(<NotInsertion />);
});
});
});
describe('useLayoutEffect', () => {
@@ -148,6 +148,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
function Text({children = null, text}) {
Scheduler.log(`Text:${text} render`);
React.useInsertionEffect(() => {
Scheduler.log(`Text:${text} create insertion`);
return () => {
Scheduler.log(`Text:${text} destroy insertion`);
};
}, []);
React.useLayoutEffect(() => {
Scheduler.log(`Text:${text} create layout`);
return () => {
@@ -268,6 +275,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Suspend:Async',
'Text:Fallback render',
'Text:Outside render',
'Text:Fallback create insertion',
'Text:Outside create insertion',
'Text:Fallback create layout',
'Text:Outside create layout',
'App create layout',
@@ -298,7 +307,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inside:Before render',
'AsyncText:Async render',
'ClassText:Inside:After render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'Text:Inside:Before create insertion',
'Text:Inside:Before create layout',
'AsyncText:Async create layout',
'ClassText:Inside:After componentDidMount',
@@ -320,9 +331,11 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'App destroy layout',
'Text:Inside:Before destroy insertion',
'Text:Inside:Before destroy layout',
'AsyncText:Async destroy layout',
'ClassText:Inside:After componentWillUnmount',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'App destroy passive',
'Text:Inside:Before destroy passive',
@@ -395,6 +408,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ClassText:Inside:After render',
'Text:Fallback render',
'Text:Outside render',
'Text:Inside:Before create insertion',
'Text:Fallback create insertion',
'Text:Outside create insertion',
'Text:Inside:Before create layout',
'ClassText:Inside:After componentDidMount',
'Text:Fallback create layout',
@@ -420,6 +436,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'AsyncText:Async render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'Text:Fallback destroy passive',
@@ -439,9 +456,11 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'App destroy layout',
'Text:Inside:Before destroy insertion',
'Text:Inside:Before destroy layout',
'AsyncText:Async destroy layout',
'ClassText:Inside:After componentWillUnmount',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'App destroy passive',
'Text:Inside:Before destroy passive',
@@ -452,7 +471,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
});
describe('layout effects within a tree that re-suspends in an update', () => {
describe('effects within a tree that re-suspends in an update', () => {
// @gate enableLegacyCache && !disableLegacyMode
it('should not be destroyed or recreated in legacy roots', async () => {
function App({children = null}) {
@@ -490,6 +509,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inside:Before render',
'Text:Inside:After render',
'Text:Outside render',
'Text:Inside:Before create insertion',
'Text:Inside:After create insertion',
'Text:Outside create insertion',
'Text:Inside:Before create layout',
'Text:Inside:After create layout',
'Text:Outside create layout',
@@ -522,6 +544,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inside:After render',
'Text:Fallback render',
'Text:Outside render',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
]);
@@ -553,6 +576,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'AsyncText:Async render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'Text:Fallback destroy passive',
@@ -572,9 +596,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'App destroy layout',
'Text:Inside:Before destroy insertion',
'Text:Inside:Before destroy layout',
'AsyncText:Async destroy layout',
'Text:Inside:After destroy insertion',
'Text:Inside:After destroy layout',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'App destroy passive',
'Text:Inside:Before destroy passive',
@@ -620,6 +647,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inside:Before render',
'Text:Inside:After render',
'Text:Outside render',
'Text:Inside:Before create insertion',
'Text:Inside:After create insertion',
'Text:Outside create insertion',
'Text:Inside:Before create layout',
'Text:Inside:After create layout',
'Text:Outside create layout',
@@ -652,6 +682,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Outside render',
'Text:Inside:Before destroy layout',
'Text:Inside:After destroy layout',
'Text:Fallback create insertion',
'Text:Fallback create layout',
]);
await waitForAll([
@@ -683,6 +714,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inside:Before render',
'AsyncText:Async render',
'Text:Inside:After render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'Text:Inside:Before create layout',
'AsyncText:Async create layout',
@@ -704,9 +736,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'App destroy layout',
'Text:Inside:Before destroy insertion',
'Text:Inside:Before destroy layout',
'AsyncText:Async destroy layout',
'Text:Inside:After destroy insertion',
'Text:Inside:After destroy layout',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'App destroy passive',
'Text:Inside:Before destroy passive',
@@ -893,6 +928,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'App render',
'Text:Outer render',
'Text:Inner render',
'Text:Inner create insertion',
'Text:Outer create insertion',
'Text:Inner create layout',
'Text:Outer create layout',
'App create layout',
@@ -919,6 +956,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback render',
'Text:Outer destroy layout',
'Text:Inner destroy layout',
'Text:Fallback create insertion',
'Text:Fallback create layout',
]);
await waitForAll([
@@ -946,6 +984,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'AsyncText:Async render',
'Text:Outer render',
'Text:Inner render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'Text:Inner create layout',
@@ -968,7 +1007,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'App destroy layout',
'AsyncText:Async destroy layout',
'Text:Outer destroy insertion',
'Text:Outer destroy layout',
'Text:Inner destroy insertion',
'Text:Inner destroy layout',
'App destroy passive',
'AsyncText:Async destroy passive',
@@ -1013,6 +1054,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'App render',
'Text:Outer render',
'Text:MemoizedInner render',
'Text:MemoizedInner create insertion',
'Text:Outer create insertion',
'Text:MemoizedInner create layout',
'Text:Outer create layout',
'App create layout',
@@ -1040,6 +1083,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback render',
'Text:Outer destroy layout',
'Text:MemoizedInner destroy layout',
'Text:Fallback create insertion',
'Text:Fallback create layout',
]);
await waitForAll([
@@ -1066,6 +1110,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'AsyncText:Async render',
'Text:Outer render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'Text:MemoizedInner create layout',
@@ -1088,7 +1133,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'App destroy layout',
'AsyncText:Async destroy layout',
'Text:Outer destroy insertion',
'Text:Outer destroy layout',
'Text:MemoizedInner destroy insertion',
'Text:MemoizedInner destroy layout',
'App destroy passive',
'AsyncText:Async destroy passive',
@@ -1119,6 +1166,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Outer render',
'Text:Inner render',
'Text:Outer create insertion',
'Text:Inner create insertion',
'Text:Outer create layout',
'Text:Inner create layout',
'Text:Outer create passive',
@@ -1143,6 +1192,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Suspend:InnerAsync_1',
'Text:InnerFallback render',
'Text:Inner destroy layout',
'Text:InnerFallback create insertion',
'Text:InnerFallback create layout',
'Text:InnerFallback create passive',
@@ -1175,6 +1225,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:OuterFallback render',
'Text:Outer destroy layout',
'Text:InnerFallback destroy layout',
'Text:OuterFallback create insertion',
'Text:OuterFallback create layout',
'Text:OuterFallback create passive',
@@ -1267,7 +1318,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inner render',
'Suspend:InnerAsync_2',
'Text:InnerFallback render',
'Text:OuterFallback destroy insertion',
'Text:OuterFallback destroy layout',
'Text:Outer create layout',
'AsyncText:OuterAsync_1 create layout',
@@ -1295,6 +1346,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Inner render',
'AsyncText:InnerAsync_2 render',
'Text:InnerFallback destroy insertion',
'Text:InnerFallback destroy layout',
'Text:Inner create layout',
'AsyncText:InnerAsync_2 create layout',
@@ -1327,6 +1379,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'AsyncText:OuterAsync_1 destroy layout',
'Text:Inner destroy layout',
'AsyncText:InnerAsync_2 destroy layout',
'Text:OuterFallback create insertion',
'Text:OuterFallback create layout',
'Text:OuterFallback create passive',
@@ -1358,6 +1411,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'AsyncText:OuterAsync_2 render',
'Text:Inner render',
'AsyncText:InnerAsync_2 render',
'Text:OuterFallback destroy insertion',
'Text:OuterFallback destroy layout',
'Text:Outer create layout',
'AsyncText:OuterAsync_2 create layout',
@@ -1397,6 +1451,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Outer render',
'Text:Inner render',
'Text:Outer create insertion',
'Text:Inner create insertion',
'Text:Outer create layout',
'Text:Inner create layout',
'Text:Outer create passive',
@@ -1421,6 +1477,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Suspend:InnerAsync_1',
'Text:InnerFallback render',
'Text:Inner destroy layout',
'Text:InnerFallback create insertion',
'Text:InnerFallback create layout',
'Text:InnerFallback create passive',
@@ -1452,6 +1509,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:OuterFallback render',
'Text:Outer destroy layout',
'Text:InnerFallback destroy layout',
'Text:OuterFallback create insertion',
'Text:OuterFallback create layout',
'Text:OuterFallback create passive',
@@ -1484,7 +1542,11 @@ describe('ReactSuspenseEffectsSemantics', () => {
'AsyncText:OuterAsync_1 render',
'Text:Inner render',
'AsyncText:InnerAsync_1 render',
'Text:OuterFallback destroy insertion',
'Text:OuterFallback destroy layout',
...(gate(flags => flags.enableHiddenSubtreeInsertionEffectCleanup)
? ['Text:InnerFallback destroy insertion']
: []),
'Text:Outer create layout',
'AsyncText:OuterAsync_1 create layout',
'Text:Inner create layout',
@@ -1534,6 +1596,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'Text:Inside create layout',
'Text:Outside create layout',
'Text:Inside create passive',
@@ -1558,6 +1622,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback:Outside render',
'Text:Outside render',
'Text:Inside destroy layout',
'Text:Fallback:Inside create insertion',
'Text:Fallback:Outside create insertion',
'Text:Fallback:Inside create layout',
'Text:Fallback:Outside create layout',
]);
@@ -1596,6 +1662,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback:Outside render',
'Text:Outside render',
'Text:Fallback:Inside destroy layout',
'Text:Fallback:Fallback create insertion',
'Text:Fallback:Fallback create layout',
]);
await waitForAll([
@@ -1629,7 +1696,12 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Inside render',
'AsyncText:OutsideAsync render',
...(gate(flags => flags.enableHiddenSubtreeInsertionEffectCleanup)
? ['Text:Fallback:Inside destroy insertion']
: []),
'Text:Fallback:Fallback destroy insertion',
'Text:Fallback:Fallback destroy layout',
'Text:Fallback:Outside destroy insertion',
'Text:Fallback:Outside destroy layout',
'Text:Inside create layout',
'AsyncText:OutsideAsync create layout',
@@ -1677,6 +1749,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'Text:Inside create layout',
'Text:Outside create layout',
'Text:Inside create passive',
@@ -1707,6 +1781,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback:Outside render',
'Text:Outside render',
'Text:Inside destroy layout',
'Text:Fallback:Fallback create insertion',
'Text:Fallback:Outside create insertion',
'Text:Fallback:Fallback create layout',
'Text:Fallback:Outside create layout',
'Text:Fallback:Fallback create passive',
@@ -1737,7 +1813,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Fallback:Inside render',
'AsyncText:FallbackAsync render',
'Text:Fallback:Fallback destroy insertion',
'Text:Fallback:Fallback destroy layout',
'Text:Fallback:Inside create insertion',
'Text:Fallback:Inside create layout',
'AsyncText:FallbackAsync create layout',
'Text:Fallback:Fallback destroy passive',
@@ -1761,8 +1839,10 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Inside render',
'AsyncText:OutsideAsync render',
'Text:Fallback:Inside destroy insertion',
'Text:Fallback:Inside destroy layout',
'AsyncText:FallbackAsync destroy layout',
'Text:Fallback:Outside destroy insertion',
'Text:Fallback:Outside destroy layout',
'Text:Inside create layout',
'AsyncText:OutsideAsync create layout',
@@ -1807,6 +1887,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'Text:Inside create layout',
'Text:Outside create layout',
'Text:Inside create passive',
@@ -1828,6 +1910,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback render',
'Text:Outside render',
'Text:Inside destroy layout',
'Text:Fallback create insertion',
'Text:Fallback create layout',
]);
await waitForAll([
@@ -1850,6 +1933,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'Text:Inside render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'Text:Inside create layout',
'Text:Fallback destroy passive',
@@ -1916,6 +2000,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ThrowsInDidMount render',
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'ThrowsInDidMount componentDidMount',
'Text:Inside create layout',
'Text:Outside create layout',
@@ -1949,6 +2035,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Outside render',
'ThrowsInDidMount componentWillUnmount',
'Text:Inside destroy layout',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
@@ -1974,6 +2061,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'AsyncText:Async render',
'ThrowsInDidMount render',
'Text:Inside render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
@@ -1986,11 +2074,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback destroy passive',
'AsyncText:Async create passive',
// Destroy layout and passive effects in the errored tree.
// Destroy insertion, layout, and passive effects in the errored tree.
'App destroy layout',
'AsyncText:Async destroy layout',
'ThrowsInDidMount componentWillUnmount',
'Text:Inside destroy insertion',
'Text:Inside destroy layout',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'AsyncText:Async destroy passive',
'Text:Inside destroy passive',
@@ -1999,6 +2089,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
// Render fallback
'ErrorBoundary render: catch',
'Text:Error render',
'Text:Error create insertion',
'Text:Error create layout',
'Text:Error create passive',
]);
@@ -2054,6 +2145,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ThrowsInWillUnmount render',
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'ThrowsInWillUnmount componentDidMount',
'Text:Inside create layout',
'Text:Outside create layout',
@@ -2092,12 +2185,18 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inside destroy layout',
// Finish the in-progress commit
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
// Destroy layout and passive effects in the errored tree.
'App destroy layout',
...(gate(flags => flags.enableHiddenSubtreeInsertionEffectCleanup)
? ['Text:Inside destroy insertion']
: []),
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'Text:Inside destroy passive',
'Text:Fallback destroy passive',
@@ -2106,6 +2205,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
// Render fallback
'ErrorBoundary render: catch',
'Text:Error render',
'Text:Error create insertion',
'Text:Error create layout',
'Text:Error create passive',
]);
@@ -2163,6 +2263,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ThrowsInLayoutEffect render',
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'ThrowsInLayoutEffect useLayoutEffect create',
'Text:Inside create layout',
'Text:Outside create layout',
@@ -2196,6 +2298,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Outside render',
'ThrowsInLayoutEffect useLayoutEffect destroy',
'Text:Inside destroy layout',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
@@ -2226,6 +2329,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ThrowsInLayoutEffect render',
'Text:Inside render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
// Even though an error was thrown in useLayoutEffect,
@@ -2241,7 +2345,9 @@ describe('ReactSuspenseEffectsSemantics', () => {
// Destroy layout and passive effects in the errored tree.
'App destroy layout',
'AsyncText:Async destroy layout',
'Text:Inside destroy insertion',
'Text:Inside destroy layout',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'AsyncText:Async destroy passive',
'Text:Inside destroy passive',
@@ -2250,6 +2356,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
// Render fallback
'ErrorBoundary render: catch',
'Text:Error render',
'Text:Error create insertion',
'Text:Error create layout',
'Text:Error create passive',
]);
@@ -2305,6 +2412,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ThrowsInLayoutEffectDestroy render',
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'ThrowsInLayoutEffectDestroy useLayoutEffect create',
'Text:Inside create layout',
'Text:Outside create layout',
@@ -2343,12 +2452,18 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Inside destroy layout',
// Finish the in-progress commit
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
// Destroy layout and passive effects in the errored tree.
'App destroy layout',
...(gate(flags => flags.enableHiddenSubtreeInsertionEffectCleanup)
? ['Text:Inside destroy insertion']
: []),
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'Text:Inside destroy passive',
'Text:Fallback destroy passive',
@@ -2357,6 +2472,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
// Render fallback
'ErrorBoundary render: catch',
'Text:Error render',
'Text:Error create insertion',
'Text:Error create layout',
'Text:Error create passive',
]);
@@ -2402,6 +2518,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'Text:Function render',
'ClassText:Class render',
'Text:Function create insertion',
'Text:Function create layout',
'ClassText:Class componentDidMount',
'Text:Function create passive',
@@ -2503,6 +2620,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
ReactNoop.render(null);
});
assertLog([
'Text:Function destroy insertion',
'Text:Function destroy layout',
'AsyncText:Async_1 destroy layout',
'AsyncText:Async_2 destroy layout',
@@ -2562,6 +2680,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Function render',
'Suspender "null" render',
'ClassText:Class render',
'Text:Function create insertion',
'Text:Function create layout',
'ClassText:Class componentDidMount',
'Text:Function create passive',
@@ -2657,6 +2776,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
ReactNoop.render(null);
});
assertLog([
'Text:Function destroy insertion',
'Text:Function destroy layout',
'ClassText:Class componentWillUnmount',
'Text:Function destroy passive',
@@ -2774,6 +2894,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ClassComponent:refCallback render',
'RefCheckerInner:refCallback render',
'Text:Fallback render',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
]);
@@ -2785,6 +2906,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
});
assertLog([
'AsyncText:Async render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'Text:Fallback destroy passive',
@@ -2852,6 +2974,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'RefCheckerInner:refObject destroy layout ref? false',
'RefCheckerOuter refCallback value? false',
'RefCheckerInner:refCallback destroy layout ref? false',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
@@ -2881,6 +3004,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'RefCheckerOuter render',
'RefCheckerInner:refObject render',
'RefCheckerInner:refCallback render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'RefCheckerInner:refObject create layout ref? false',
@@ -2962,6 +3086,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'RefCheckerInner:refObject destroy layout ref? false',
'RefCheckerOuter refCallback value? false',
'RefCheckerInner:refCallback destroy layout ref? false',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
@@ -2989,6 +3114,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'RefCheckerInner:refObject render',
'ClassComponent:refCallback render',
'RefCheckerInner:refCallback render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'RefCheckerInner:refObject create layout ref? false',
@@ -3070,6 +3196,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'RefCheckerInner:refObject destroy layout ref? false',
'RefCheckerOuter refCallback value? false',
'RefCheckerInner:refCallback destroy layout ref? false',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
@@ -3097,6 +3224,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'RefCheckerInner:refObject render',
'FunctionComponent render',
'RefCheckerInner:refCallback render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'RefCheckerInner:refObject create layout ref? false',
@@ -3180,6 +3308,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Suspend:Async',
'Text:Fallback render',
'RefChecker destroy layout ref? true',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
@@ -3196,6 +3325,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
assertLog([
'AsyncText:Async render',
'RefChecker render',
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'RefChecker create layout ref? true',
@@ -3265,6 +3395,8 @@ describe('ReactSuspenseEffectsSemantics', () => {
'ThrowsInRefCallback render',
'Text:Inside render',
'Text:Outside render',
'Text:Inside create insertion',
'Text:Outside create insertion',
'ThrowsInRefCallback refCallback ref? true',
'Text:Inside create layout',
'Text:Outside create layout',
@@ -3298,6 +3430,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Outside render',
'ThrowsInRefCallback refCallback ref? false',
'Text:Inside destroy layout',
'Text:Fallback create insertion',
'Text:Fallback create layout',
'Text:Fallback create passive',
@@ -3330,6 +3463,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
// Even though an error was thrown in refCallback,
// subsequent layout effects should still be created.
'Text:Fallback destroy insertion',
'Text:Fallback destroy layout',
'AsyncText:Async create layout',
'ThrowsInRefCallback refCallback ref? true',
@@ -3339,11 +3473,13 @@ describe('ReactSuspenseEffectsSemantics', () => {
'Text:Fallback destroy passive',
'AsyncText:Async create passive',
// Destroy layout and passive effects in the errored tree.
// Destroy insertion, layout, and passive effects in the errored tree.
'App destroy layout',
'AsyncText:Async destroy layout',
'ThrowsInRefCallback refCallback ref? false',
'Text:Inside destroy insertion',
'Text:Inside destroy layout',
'Text:Outside destroy insertion',
'Text:Outside destroy layout',
'AsyncText:Async destroy passive',
'Text:Inside destroy passive',
@@ -3352,6 +3488,7 @@ describe('ReactSuspenseEffectsSemantics', () => {
// Render fallback
'ErrorBoundary render: catch',
'Text:Error render',
'Text:Error create insertion',
'Text:Error create layout',
'Text:Error create passive',
]);
+5
View File
@@ -172,6 +172,11 @@ export const transitionLaneExpirationMs = 5000;
// Renames the internal symbol for elements since they have changed signature/constructor
export const renameElementSymbol = true;
/**
* Enables a fix to run insertion effect cleanup on hidden subtrees.
*/
export const enableHiddenSubtreeInsertionEffectCleanup = false;
/**
* Removes legacy style context defined using static `contextTypes` and consumed with static `childContextTypes`.
*/
@@ -20,6 +20,7 @@
export const alwaysThrottleRetries = __VARIANT__;
export const enableAddPropertiesFastPath = __VARIANT__;
export const enableObjectFiber = __VARIANT__;
export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__;
export const enablePersistedModeClonedFlag = __VARIANT__;
export const enableShallowPropDiffing = __VARIANT__;
export const passChildrenWhenCloningPersistedNodes = __VARIANT__;
@@ -22,6 +22,7 @@ export const {
alwaysThrottleRetries,
enableAddPropertiesFastPath,
enableFabricCompleteRootInCommitPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enablePersistedModeClonedFlag,
enableShallowPropDiffing,
@@ -50,6 +50,7 @@ export const enableFizzExternalRuntime = true;
export const enableFlightReadableStream = true;
export const enableGetInspectorDataForInstanceInProduction = false;
export const enableHalt = false;
export const enableHiddenSubtreeInsertionEffectCleanup = false;
export const enableInfiniteRenderLoopDetection = true;
export const enableLazyContextPropagation = false;
export const enableContextProfiling = false;
@@ -46,6 +46,7 @@ export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = true;
export const enableGetInspectorDataForInstanceInProduction = false;
export const enableFabricCompleteRootInCommitPhase = false;
export const enableHiddenSubtreeInsertionEffectCleanup = false;
export const enableRetryLaneExpiration = false;
export const retryLaneExpirationMs = 5000;
@@ -44,6 +44,7 @@ export const enableHalt = false;
export const enableInfiniteRenderLoopDetection = true;
export const enableLazyContextPropagation = false;
export const enableContextProfiling = false;
export const enableHiddenSubtreeInsertionEffectCleanup = true;
export const enableLegacyCache = false;
export const enableLegacyFBSupport = false;
export const enableLegacyHidden = false;
@@ -49,6 +49,7 @@ export const enableFilterEmptyStringAttributesDOM = true;
export const enableGetInspectorDataForInstanceInProduction = false;
export const enableRenderableContext = false;
export const enableFabricCompleteRootInCommitPhase = false;
export const enableHiddenSubtreeInsertionEffectCleanup = true;
export const enableRetryLaneExpiration = false;
export const retryLaneExpirationMs = 5000;
@@ -21,6 +21,7 @@ export const disableSchedulerTimeoutInWorkLoop = __VARIANT__;
export const enableAddPropertiesFastPath = __VARIANT__;
export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
export const enableDO_NOT_USE_disableStrictPassiveEffect = __VARIANT__;
export const enableHiddenSubtreeInsertionEffectCleanup = __VARIANT__;
export const enableNoCloningMemoCache = __VARIANT__;
export const enableObjectFiber = __VARIANT__;
export const enableRenderableContext = __VARIANT__;
@@ -30,6 +30,7 @@ export const {
enableRetryLaneExpiration,
enableTransitionTracing,
enableTrustedTypesIntegration,
enableHiddenSubtreeInsertionEffectCleanup,
favorSafetyOverHydrationPerf,
renameElementSymbol,
retryLaneExpirationMs,