diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js
index 582cefc7aaa..b0026d1393a 100644
--- a/packages/react-reconciler/src/ReactFiberBeginWork.js
+++ b/packages/react-reconciler/src/ReactFiberBeginWork.js
@@ -262,7 +262,7 @@ function updateFunctionalComponent(
nextProps: any,
renderExpirationTime,
) {
- const unmaskedContext = getUnmaskedContext(workInProgress, Component);
+ const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
const context = getMaskedContext(workInProgress, unmaskedContext);
let nextChildren;
@@ -674,7 +674,7 @@ function mountIndeterminateComponent(
}
}
- const unmaskedContext = getUnmaskedContext(workInProgress, Component);
+ const unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
const context = getMaskedContext(workInProgress, unmaskedContext);
prepareToReadContext(workInProgress, renderExpirationTime);
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index a4b3692a5af..34a1c5a37d2 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -473,7 +473,7 @@ function constructClassInstance(
props: any,
renderExpirationTime: ExpirationTime,
): any {
- const unmaskedContext = getUnmaskedContext(workInProgress, ctor);
+ const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
const contextTypes = ctor.contextTypes;
const isContextConsumer = contextTypes !== null && contextTypes !== undefined;
const context = isContextConsumer
@@ -666,7 +666,7 @@ function mountClassInstance(
}
const instance = workInProgress.stateNode;
- const unmaskedContext = getUnmaskedContext(workInProgress, ctor);
+ const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
instance.props = newProps;
instance.state = workInProgress.memoizedState;
@@ -758,7 +758,11 @@ function resumeMountClassInstance(
instance.props = oldProps;
const oldContext = instance.context;
- const nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor);
+ const nextLegacyUnmaskedContext = getUnmaskedContext(
+ workInProgress,
+ ctor,
+ true,
+ );
const nextLegacyContext = getMaskedContext(
workInProgress,
nextLegacyUnmaskedContext,
@@ -897,7 +901,11 @@ function updateClassInstance(
instance.props = oldProps;
const oldContext = instance.context;
- const nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor);
+ const nextLegacyUnmaskedContext = getUnmaskedContext(
+ workInProgress,
+ ctor,
+ true,
+ );
const nextLegacyContext = getMaskedContext(
workInProgress,
nextLegacyUnmaskedContext,
diff --git a/packages/react-reconciler/src/ReactFiberContext.js b/packages/react-reconciler/src/ReactFiberContext.js
index 0a12e724b16..58c648ad762 100644
--- a/packages/react-reconciler/src/ReactFiberContext.js
+++ b/packages/react-reconciler/src/ReactFiberContext.js
@@ -49,11 +49,11 @@ let previousContext: Object = emptyContextObject;
function getUnmaskedContext(
workInProgress: Fiber,
Component: Function,
+ didPushOwnContextIfProvider: boolean,
): Object {
- const hasOwnContext = isContextProvider(Component);
- if (hasOwnContext) {
+ if (didPushOwnContextIfProvider && isContextProvider(Component)) {
// If the fiber is a context provider itself, when we read its context
- // we have already pushed its own child context on the stack. A context
+ // we may have already pushed its own child context on the stack. A context
// provider should not "see" its own child context. Therefore we read the
// previous (parent) context instead for a context provider.
return previousContext;
diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
index 0ffd7079d73..6388796b163 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
@@ -2030,6 +2030,39 @@ describe('ReactIncremental', () => {
]);
});
+ it('does not leak own context into context provider (factory components)', () => {
+ const ops = [];
+ function Recurse(props, context) {
+ return {
+ getChildContext() {
+ return {n: (context.n || 3) - 1};
+ },
+ render() {
+ ops.push('Recurse ' + JSON.stringify(context));
+ if (context.n === 0) {
+ return null;
+ }
+ return ;
+ },
+ };
+ }
+ Recurse.contextTypes = {
+ n: PropTypes.number,
+ };
+ Recurse.childContextTypes = {
+ n: PropTypes.number,
+ };
+
+ ReactNoop.render();
+ ReactNoop.flush();
+ expect(ops).toEqual([
+ 'Recurse {}',
+ 'Recurse {"n":2}',
+ 'Recurse {"n":1}',
+ 'Recurse {"n":0}',
+ ]);
+ });
+
it('provides context when reusing work', () => {
const ops = [];