Skip to content

Commit c77a203

Browse files
committed
Add protection against multiple roots
1 parent 44bd862 commit c77a203

File tree

4 files changed

+58
-16
lines changed

4 files changed

+58
-16
lines changed

fixtures/packaging/babel-standalone/dev.html

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,41 @@
55
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
66
<div id="container"></div>
77
<script type="text/babel">
8-
ReactDOM.render(
9-
<h1>Hello World!</h1>,
10-
document.getElementById('container')
11-
);
8+
function App({ id }) {
9+
const [x, setX] = React.useState(0)
10+
const [y, setY] = React.useState(0)
11+
React.useEffect(() => {
12+
if (x < 10)
13+
setX(x + 1)
14+
}, [x])
15+
return <h1>Root {id} [{x}]</h1>
16+
}
17+
18+
let roots = []
19+
for (let i = 0; i < 100; i++) {
20+
let child = document.createElement('div')
21+
document.body.appendChild(child)
22+
//roots[i] = child
23+
// ReactDOM.render(
24+
// <App id={i} />,
25+
// child
26+
// );
27+
roots[i] = ReactDOM.createRoot(child)
28+
roots[i].render(<App id={i} />)
29+
}
30+
31+
let iter = 1
32+
33+
document.body.addEventListener('click', () => {
34+
iter++
35+
for (let i = 0; i < 100; i++) {
36+
// ReactDOM.render(
37+
// <App id={iter + '_' + i} />,
38+
// roots[i]
39+
//);
40+
roots[i].render(<App id={iter + '_' + i} />)
41+
}
42+
})
1243
</script>
1344
</body>
1445
</html>

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ let didScheduleUpdateDuringPassiveEffects = false;
401401

402402
const NESTED_PASSIVE_UPDATE_LIMIT = 50;
403403
let nestedPassiveUpdateCount: number = 0;
404+
let rootWithPassiveNestedUpdates: FiberRoot | null = null;
404405

405406
// If two updates are scheduled within the same event, we should treat their
406407
// event times as simultaneous, even if the actual clock time has advanced
@@ -2214,6 +2215,7 @@ function commitRootImpl(
22142215
releaseRootPooledCache(root, remainingLanes);
22152216
if (__DEV__) {
22162217
nestedPassiveUpdateCount = 0;
2218+
rootWithPassiveNestedUpdates = null;
22172219
}
22182220
}
22192221

@@ -2481,7 +2483,12 @@ function flushPassiveEffectsImpl() {
24812483
// If additional passive effects were scheduled, increment a counter. If this
24822484
// exceeds the limit, we'll fire a warning.
24832485
if (didScheduleUpdateDuringPassiveEffects) {
2484-
nestedPassiveUpdateCount++;
2486+
if (root === rootWithPassiveNestedUpdates) {
2487+
nestedPassiveUpdateCount++;
2488+
} else {
2489+
nestedPassiveUpdateCount = 0;
2490+
rootWithPassiveNestedUpdates = root;
2491+
}
24852492
} else {
24862493
nestedPassiveUpdateCount = 0;
24872494
}
@@ -2760,6 +2767,8 @@ function checkForNestedUpdates() {
27602767
if (__DEV__) {
27612768
if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {
27622769
nestedPassiveUpdateCount = 0;
2770+
rootWithPassiveNestedUpdates = null;
2771+
27632772
console.error(
27642773
'Maximum update depth exceeded. This can happen when a component ' +
27652774
"calls setState inside useEffect, but useEffect either doesn't " +

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ let didScheduleUpdateDuringPassiveEffects = false;
401401

402402
const NESTED_PASSIVE_UPDATE_LIMIT = 50;
403403
let nestedPassiveUpdateCount: number = 0;
404+
let rootWithPassiveNestedUpdates: FiberRoot | null = null;
404405

405406
// If two updates are scheduled within the same event, we should treat their
406407
// event times as simultaneous, even if the actual clock time has advanced
@@ -2214,6 +2215,7 @@ function commitRootImpl(
22142215
releaseRootPooledCache(root, remainingLanes);
22152216
if (__DEV__) {
22162217
nestedPassiveUpdateCount = 0;
2218+
rootWithPassiveNestedUpdates = null;
22172219
}
22182220
}
22192221

@@ -2481,7 +2483,12 @@ function flushPassiveEffectsImpl() {
24812483
// If additional passive effects were scheduled, increment a counter. If this
24822484
// exceeds the limit, we'll fire a warning.
24832485
if (didScheduleUpdateDuringPassiveEffects) {
2484-
nestedPassiveUpdateCount++;
2486+
if (root === rootWithPassiveNestedUpdates) {
2487+
nestedPassiveUpdateCount++;
2488+
} else {
2489+
nestedPassiveUpdateCount = 0;
2490+
rootWithPassiveNestedUpdates = root;
2491+
}
24852492
} else {
24862493
nestedPassiveUpdateCount = 0;
24872494
}
@@ -2760,6 +2767,8 @@ function checkForNestedUpdates() {
27602767
if (__DEV__) {
27612768
if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {
27622769
nestedPassiveUpdateCount = 0;
2770+
rootWithPassiveNestedUpdates = null;
2771+
27632772
console.error(
27642773
'Maximum update depth exceeded. This can happen when a component ' +
27652774
"calls setState inside useEffect, but useEffect either doesn't " +

packages/use-sync-external-store/src/__tests__/useSyncExternalStoreShared-test.js

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -584,16 +584,9 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
584584
'calls setState inside componentWillUpdate or componentDidUpdate. React limits ' +
585585
'the number of nested updates to prevent infinite loops.',
586586
);
587-
}).toErrorDev(
588-
gate(flags => flags.enableUseSyncExternalStoreShim)
589-
? [
590-
'The result of getSnapshot should be cached to avoid an infinite loop',
591-
]
592-
: [
593-
'Maximum update depth exceeded.',
594-
'The result of getSnapshot should be cached to avoid an infinite loop',
595-
],
596-
);
587+
}).toErrorDev([
588+
'The result of getSnapshot should be cached to avoid an infinite loop',
589+
]);
597590
});
598591

599592
test('getSnapshot can return NaN without infinite loop warning', async () => {

0 commit comments

Comments
 (0)