Skip to content

Commit

Permalink
Prevent infinite re-renders in StrictMode + Offscreen (#25203)
Browse files Browse the repository at this point in the history
* Prevent infinite re-render in StrictMode + Offscreen

* Only fire effects for Offscreen when it is revealed

* Move setting debug fiber into if branch

* Move settings of debug fiber out of if branch
  • Loading branch information
sammy-SC committed Sep 9, 2022
1 parent 8003ab9 commit 269c4e9
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 2 deletions.
9 changes: 8 additions & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ import {
LayoutMask,
PassiveMask,
PlacementDEV,
Visibility,
} from './ReactFiberFlags';
import {
NoLanes,
Expand Down Expand Up @@ -3184,9 +3185,15 @@ function doubleInvokeEffectsInDEV(
) {
const isStrictModeFiber = fiber.type === REACT_STRICT_MODE_TYPE;
const isInStrictMode = parentIsInStrictMode || isStrictModeFiber;

if (fiber.flags & PlacementDEV || fiber.tag === OffscreenComponent) {
setCurrentDebugFiberInDEV(fiber);
if (isInStrictMode) {
const isNotOffscreen = fiber.tag !== OffscreenComponent;
// Checks if Offscreen is being revealed. For all other components, evaluates to true.
const hasOffscreenBecomeVisible =
isNotOffscreen ||
(fiber.flags & Visibility && fiber.memoizedState === null);
if (isInStrictMode && hasOffscreenBecomeVisible) {
disappearLayoutEffects(fiber);
disconnectPassiveEffect(fiber);
reappearLayoutEffects(root, fiber.alternate, fiber, false);
Expand Down
9 changes: 8 additions & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ import {
LayoutMask,
PassiveMask,
PlacementDEV,
Visibility,
} from './ReactFiberFlags';
import {
NoLanes,
Expand Down Expand Up @@ -3184,9 +3185,15 @@ function doubleInvokeEffectsInDEV(
) {
const isStrictModeFiber = fiber.type === REACT_STRICT_MODE_TYPE;
const isInStrictMode = parentIsInStrictMode || isStrictModeFiber;

if (fiber.flags & PlacementDEV || fiber.tag === OffscreenComponent) {
setCurrentDebugFiberInDEV(fiber);
if (isInStrictMode) {
const isNotOffscreen = fiber.tag !== OffscreenComponent;
// Checks if Offscreen is being revealed. For all other components, evaluates to true.
const hasOffscreenBecomeVisible =
isNotOffscreen ||
(fiber.flags & Visibility && fiber.memoizedState === null);
if (isInStrictMode && hasOffscreenBecomeVisible) {
disappearLayoutEffects(fiber);
disconnectPassiveEffect(fiber);
reappearLayoutEffects(root, fiber.alternate, fiber, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ describe('ReactOffscreenStrictMode', () => {

log = [];

act(() => {
ReactNoop.render(
<React.StrictMode>
<Offscreen mode="hidden">
<Component label="A" />
<Component label="B" />
</Offscreen>
</React.StrictMode>,
);
});

expect(log).toEqual(['A: render', 'A: render', 'B: render', 'B: render']);

log = [];

act(() => {
ReactNoop.render(
<React.StrictMode>
Expand All @@ -91,5 +106,51 @@ describe('ReactOffscreenStrictMode', () => {
'A: useLayoutEffect mount',
'A: useEffect mount',
]);

log = [];

act(() => {
ReactNoop.render(
<React.StrictMode>
<Offscreen mode="hidden">
<Component label="A" />
</Offscreen>
</React.StrictMode>,
);
});

expect(log).toEqual([
'A: useLayoutEffect unmount',
'A: useEffect unmount',
'A: render',
'A: render',
]);
});

it('should not cause infinite render loop when StrictMode is used with Suspense and synchronous set states', () => {
// This is a regression test, see https://github.com/facebook/react/pull/25179 for more details.
function App() {
const [state, setState] = React.useState(false);

React.useLayoutEffect(() => {
setState(true);
}, []);

React.useEffect(() => {
// Empty useEffect with empty dependency array is needed to trigger infinite render loop.
}, []);

return state;
}

act(() => {
ReactNoop.render(
<React.StrictMode>
<React.Suspense>
<App />
</React.Suspense>
</React.StrictMode>,
);
});
});
});

0 comments on commit 269c4e9

Please sign in to comment.