@samitouri / QOS-React / commits / bc1ea9cd96

Handle errors thrown in gDSFP of a module-style context provider (#13269)

Context should be pushed before calling any user code, so if it errors the stack unwinds correctly.

Andrew Clark committed Jul 25, 2018 at 14:22 UTC bc1ea9cd96c81467c574b134349eafc303835d0e
2 files changed +27 -4
packages/react-reconciler/src/ReactFiberBeginWork.js
+5 -4
@@ -618,6 +618,11 @@ function mountIndeterminateComponent(
618 // Proceed under the assumption that this is a class instance
619 workInProgress.tag = ClassComponent;
620
621 + // Push context providers early to prevent context stack mismatches.
622 + // During mounting we don't know the child context yet as the instance doesn't exist.
623 + // We will invalidate the child context in finishClassComponent() right after rendering.
624 + const hasContext = pushLegacyContextProvider(workInProgress);
625 +
626 workInProgress.memoizedState =
627 value.state !== null && value.state !== undefined ? value.state : null;
628
@@ -630,10 +635,6 @@ function mountIndeterminateComponent(
635 );
636 }
637
633 - // Push context providers early to prevent context stack mismatches.
634 - // During mounting we don't know the child context yet as the instance doesn't exist.
635 - // We will invalidate the child context in finishClassComponent() right after rendering.
636 - const hasContext = pushLegacyContextProvider(workInProgress);
638 adoptClassInstance(workInProgress, value);
639 mountClassInstance(workInProgress, renderExpirationTime);
640 return finishClassComponent(
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+22
@@ -1469,4 +1469,26 @@ describe('ReactIncrementalErrorHandling', () => {
1469 ReactNoop.flushDeferredPri();
1470 expect(ReactNoop.getChildren()).toEqual([span('Caught an error: Hello')]);
1471 });
1472 +
1473 + it('handles error thrown inside getDerivedStateFromProps of a module-style context provider', () => {
1474 + function Provider() {
1475 + return {
1476 + getChildContext() {
1477 + return {foo: 'bar'};
1478 + },
1479 + render() {
1480 + return 'Hi';
1481 + },
1482 + };
1483 + }
1484 + Provider.childContextTypes = {
1485 + x: () => {},
1486 + };
1487 + Provider.getDerivedStateFromProps = () => {
1488 + throw new Error('Oops!');
1489 + };
1490 +
1491 + ReactNoop.render(<Provider />);
1492 + expect(() => ReactNoop.flush()).toThrow('Oops!');
1493 + });
1494 });