diff --git a/src/renderers/shared/fiber/ReactFiberContext.js b/src/renderers/shared/fiber/ReactFiberContext.js index fbc478020f..5fd47f5a18 100644 --- a/src/renderers/shared/fiber/ReactFiberContext.js +++ b/src/renderers/shared/fiber/ReactFiberContext.js @@ -80,23 +80,32 @@ exports.popContextProvider = function() : void { exports.pushContextProvider = function(fiber : Fiber, didPerformWork : boolean) : void { const instance = fiber.stateNode; const childContextTypes = fiber.type.childContextTypes; - const childContext = instance.getChildContext(); - for (let contextKey in childContext) { - invariant( - contextKey in childContextTypes, - '%s.getChildContext(): key "%s" is not defined in childContextTypes.', - getComponentName(fiber), - contextKey - ); - } - if (__DEV__) { - const name = getComponentName(fiber); - const debugID = 0; // TODO: pass a real ID - checkReactTypeSpec(childContextTypes, childContext, 'childContext', name, null, debugID); + const memoizedMergedChildContext = instance.__reactInternalMemoizedMergedChildContext; + const canReuseMergedChildContext = !didPerformWork && memoizedMergedChildContext != null; + + let mergedContext = null; + if (canReuseMergedChildContext) { + mergedContext = memoizedMergedChildContext; + } else { + const childContext = instance.getChildContext(); + for (let contextKey in childContext) { + invariant( + contextKey in childContextTypes, + '%s.getChildContext(): key "%s" is not defined in childContextTypes.', + getComponentName(fiber), + contextKey + ); + } + if (__DEV__) { + const name = getComponentName(fiber); + const debugID = 0; // TODO: pass a real ID + checkReactTypeSpec(childContextTypes, childContext, 'childContext', name, null, debugID); + } + mergedContext = {...getUnmaskedContext(), ...childContext}; + instance.__reactInternalMemoizedMergedChildContext = mergedContext; } - const mergedContext = Object.assign({}, getUnmaskedContext(), childContext); index++; contextStack[index] = mergedContext; didPerformWorkStack[index] = didPerformWork; diff --git a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js index 14cc145254..6c5e04d010 100644 --- a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js +++ b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js @@ -1822,8 +1822,8 @@ describe('ReactIncremental', () => { statefulInst.setState({x: 1}); ReactNoop.flush(); expect(ops).toEqual([ - // TODO: we should be able to reuse the previous child context. - 'Intl:provide {"locale":"fr"}', + // Intl was memoized so we did not need to + // either render it or recompute its context. 'ShowLocaleClass:read {"locale":"fr"}', 'ShowLocaleFn:read {"locale":"fr"}', ]); @@ -1895,6 +1895,9 @@ describe('ReactIncremental', () => { statefulInst.setState({locale: 'gr'}); ReactNoop.flush(); expect(ops).toEqual([ + // Intl is below setState() so it might have been + // affected by it. Therefore we re-render and recompute + // its child context. 'Intl:read null', 'Intl:provide {"locale":"gr"}', 'ShowLocaleClass:read {"locale":"gr"}',