@samitouri / QOS-React-2 / commits / 8d09422424

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'];