Skip to content

Commit b57ebbf

Browse files
committed
Skip double invoking effects in Offscreen
1 parent 9fcaf88 commit b57ebbf

File tree

2 files changed

+121
-7
lines changed

2 files changed

+121
-7
lines changed

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2936,24 +2936,38 @@ function invokeEffectsInDev(
29362936

29372937
let current = firstChild;
29382938
let subtreeRoot = null;
2939+
2940+
const setNextCurrent = () => {
2941+
if (current === null) {
2942+
return;
2943+
}
2944+
if (current.sibling !== null) {
2945+
current = current.sibling;
2946+
} else {
2947+
current = subtreeRoot = current.return;
2948+
}
2949+
};
2950+
29392951
while (current !== null) {
29402952
const primarySubtreeFlag = current.subtreeFlags & fiberFlags;
29412953
if (
29422954
current !== subtreeRoot &&
29432955
current.child !== null &&
29442956
primarySubtreeFlag !== NoFlags
29452957
) {
2946-
current = current.child;
2958+
if (
2959+
current.child.tag === OffscreenComponent &&
2960+
current.child.pendingProps.mode === 'hidden'
2961+
) {
2962+
setNextCurrent();
2963+
} else {
2964+
current = current.child;
2965+
}
29472966
} else {
29482967
if ((current.flags & fiberFlags) !== NoFlags) {
29492968
invokeEffectFn(current);
29502969
}
2951-
2952-
if (current.sibling !== null) {
2953-
current = current.sibling;
2954-
} else {
2955-
current = subtreeRoot = current.return;
2956-
}
2970+
setNextCurrent();
29572971
}
29582972
}
29592973
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
let React;
2+
let Offscreen;
3+
let ReactDOMClient;
4+
let act;
5+
6+
describe('ReactOffscreenStrictMode', () => {
7+
let log;
8+
9+
beforeEach(() => {
10+
jest.resetModules();
11+
log = [];
12+
13+
React = require('react');
14+
Offscreen = React.unstable_Offscreen;
15+
ReactDOMClient = require('react-dom/client');
16+
act = require('jest-react').act;
17+
18+
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
19+
ReactFeatureFlags.enableStrictEffects = __DEV__;
20+
});
21+
22+
function Component({label}) {
23+
React.useEffect(() => {
24+
log.push(`${label}: useEffect mount`);
25+
return () => log.push(`${label}: useEffect unmount`);
26+
});
27+
28+
React.useLayoutEffect(() => {
29+
log.push(`${label}: useLayoutEffect mount`);
30+
return () => log.push(`${label}: useLayoutEffect unmount`);
31+
});
32+
33+
log.push(`${label}: render`);
34+
35+
return null;
36+
}
37+
38+
if (__DEV__) {
39+
// @gate enableOffscreen
40+
it('should trigger strict effects when offscreen is visible', () => {
41+
const container = document.createElement('div');
42+
const root = ReactDOMClient.createRoot(container, {
43+
unstable_strictMode: true,
44+
});
45+
46+
act(() => {
47+
root.render(
48+
<Offscreen mode="visible">
49+
<Component label="A" />
50+
</Offscreen>,
51+
);
52+
});
53+
54+
expect(log).toEqual([
55+
'A: render',
56+
'A: render',
57+
'A: useLayoutEffect mount',
58+
'A: useEffect mount',
59+
'A: useLayoutEffect unmount',
60+
'A: useEffect unmount',
61+
'A: useLayoutEffect mount',
62+
'A: useEffect mount',
63+
]);
64+
});
65+
66+
// @gate enableOffscreen
67+
it('should not trigger strict effects when offscreen is hidden', () => {
68+
const container = document.createElement('div');
69+
const root = ReactDOMClient.createRoot(container, {
70+
unstable_strictMode: true,
71+
});
72+
73+
act(() => {
74+
root.render(
75+
<Offscreen mode="hidden">
76+
<Component label="A" />
77+
</Offscreen>,
78+
);
79+
});
80+
81+
expect(log).toEqual(['A: render', 'A: render']);
82+
});
83+
}
84+
85+
// @gate enableOffscreen
86+
it('should default to no strinct effects', () => {
87+
const container = document.createElement('div');
88+
const root = ReactDOMClient.createRoot(container);
89+
90+
act(() => {
91+
root.render(
92+
<Offscreen mode="hidden">
93+
<Component label="A" />
94+
</Offscreen>,
95+
);
96+
});
97+
98+
expect(log).toEqual(['A: render']);
99+
});
100+
});

0 commit comments

Comments
 (0)