Fix an infinite loop in new context (#12402)

* Add a regression test for the context infinite loop

* Fix the bug

We set .return pointer inside the loop, but the top-level parent-child relationship happens outside.

This ensures the top-level parent's child points to the right copy of the parent.

Otherwise we may end up in a situation where (workInProgress === nextFiber) is never true and we loop forever.
This commit is contained in:
Dan Abramov
2018-03-20 13:06:59 +00:00
committed by GitHub
parent 1d33d6f54c
commit 2d3f2308bf
2 changed files with 39 additions and 0 deletions
+4
View File
@@ -776,6 +776,10 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
renderExpirationTime: ExpirationTime,
): void {
let fiber = workInProgress.child;
if (fiber !== null) {
// Set the return pointer of the child to the work-in-progress fiber.
fiber.return = workInProgress;
}
while (fiber !== null) {
let nextFiber;
// Visit this fiber.
@@ -762,6 +762,41 @@ describe('ReactNewContext', () => {
expect(ReactNoop.getChildren()).toEqual([span('Child')]);
});
// This is a regression case for https://github.com/facebook/react/issues/12389.
it('does not run into an infinite loop', () => {
const Context = React.createContext(null);
class App extends React.Component {
renderItem(id) {
return (
<span key={id}>
<Context.Consumer>{() => <span>inner</span>}</Context.Consumer>
<span>outer</span>
</span>
);
}
renderList() {
const list = [1, 2].map(id => this.renderItem(id));
if (this.props.reverse) {
list.reverse();
}
return list;
}
render() {
return (
<Context.Provider value={{}}>{this.renderList()}</Context.Provider>
);
}
}
ReactNoop.render(<App reverse={false} />);
ReactNoop.flush();
ReactNoop.render(<App reverse={true} />);
ReactNoop.flush();
ReactNoop.render(<App reverse={false} />);
ReactNoop.flush();
});
describe('fuzz test', () => {
const Fragment = React.Fragment;
const contextKeys = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];