mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Memoize merged child context when possible
We can avoid recreating the merged context object if the context provider work was reused. This is essential to avoid allocations for deep setState() calls.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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"}',
|
||||
|
||||
Reference in New Issue
Block a user