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.
Dan Abramov committed
Mar 20, 2018 at 13:06 UTC
8d0942242476024ac74d4ca2fcac9824b3124dc8
2 files changed
+39
packages/react-reconciler/src/ReactFiberBeginWork.js
+4
@@ -776,6 +776,10 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
776
renderExpirationTime: ExpirationTime,
777
): void {
778
let fiber = workInProgress.child;
779
+ if (fiber !== null) {
780
+ // Set the return pointer of the child to the work-in-progress fiber.
781
+ fiber.return = workInProgress;
782
+ }
783
while (fiber !== null) {
784
let nextFiber;
785
// Visit this fiber.
packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js
+35
@@ -762,6 +762,41 @@ describe('ReactNewContext', () => {
762
expect(ReactNoop.getChildren()).toEqual([span('Child')]);
763
});
764
765
+ // This is a regression case for https://github.com/facebook/react/issues/12389.
766
+ it('does not run into an infinite loop', () => {
767
+ const Context = React.createContext(null);
768
+
769
+ class App extends React.Component {
770
+ renderItem(id) {
771
+ return (
772
+ <span key={id}>
773
+ <Context.Consumer>{() => <span>inner</span>}</Context.Consumer>
774
+ <span>outer</span>
775
+ </span>
776
+ );
777
+ }
778
+ renderList() {
779
+ const list = [1, 2].map(id => this.renderItem(id));
780
+ if (this.props.reverse) {
781
+ list.reverse();
782
+ }
783
+ return list;
784
+ }
785
+ render() {
786
+ return (
787
+ <Context.Provider value={{}}>{this.renderList()}</Context.Provider>
788
+ );
789
+ }
790
+ }
791
+
792
+ ReactNoop.render(<App reverse={false} />);
793
+ ReactNoop.flush();
794
+ ReactNoop.render(<App reverse={true} />);
795
+ ReactNoop.flush();
796
+ ReactNoop.render(<App reverse={false} />);
797
+ ReactNoop.flush();
798
+ });
799
+
800
describe('fuzz test', () => {
801
const Fragment = React.Fragment;
802
const contextKeys = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];