Skip to content

Commit

Permalink
Track nested updates per root (#10574)
Browse files Browse the repository at this point in the history
We track nested updates to simulate a stack overflow error and prevent
infinite loops. Every time we commit a tree, we increment a counter.
This works if you only have one tree, but if you update many separate
trees, it creates a false negative.

The fix is to reset the counter whenever we switch trees.
  • Loading branch information
acdlite authored Aug 31, 2017
1 parent 88a079d commit a6e34cc
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 3 deletions.
24 changes: 24 additions & 0 deletions src/renderers/__tests__/ReactUpdates-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,30 @@ describe('ReactUpdates', () => {
expect(ops).toEqual(['Foo', 'Bar', 'Baz']);
});

it('can render ridiculously large number of roots without triggering infinite update loop error', () => {
class Foo extends React.Component {
componentDidMount() {
const limit = 1200;
for (let i = 0; i < limit; i++) {
if (i < limit - 1) {
ReactDOM.render(<div />, document.createElement('div'));
} else {
ReactDOM.render(<div />, document.createElement('div'), () => {
// The "nested update limit" error isn't thrown until setState
this.setState({});
});
}
}
}
render() {
return null;
}
}

const container = document.createElement('div');
ReactDOM.render(<Foo />, container);
});

it('does not fall into an infinite update loop', () => {
class NonTerminating extends React.Component {
state = {step: 0};
Expand Down
14 changes: 11 additions & 3 deletions src/renderers/shared/fiber/ReactFiberScheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(

// Use these to prevent an infinite loop of nested updates
const NESTED_UPDATE_LIMIT = 1000;
let nestedUpdateCount = 0;
let nestedUpdateCount: number = 0;
let nextRenderedTree: FiberRoot | null = null;

function resetContextStack() {
// Reset the stack
Expand Down Expand Up @@ -301,11 +302,17 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
highestPriorityRoot.current,
highestPriorityLevel,
);
if (highestPriorityRoot !== nextRenderedTree) {
// We've switched trees. Reset the nested update counter.
nestedUpdateCount = 0;
nextRenderedTree = highestPriorityRoot;
}
return;
}

nextPriorityLevel = NoWork;
nextUnitOfWork = null;
nextRenderedTree = null;
return;
}

Expand Down Expand Up @@ -969,8 +976,6 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
);
isPerformingWork = true;

nestedUpdateCount = 0;

// The priority context changes during the render phase. We'll need to
// reset it at the end.
const previousPriorityContext = priorityContext;
Expand Down Expand Up @@ -1081,6 +1086,9 @@ module.exports = function<T, P, I, TI, PI, C, CX, PL>(
firstUncaughtError = null;
capturedErrors = null;
failedBoundaries = null;
nextRenderedTree = null;
nestedUpdateCount = 0;

if (__DEV__) {
stopWorkLoopTimer();
}
Expand Down

0 comments on commit a6e34cc

Please sign in to comment.