From 972b959bd13da75cb4e01f218910f50aebf0e032 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Fri, 27 Jul 2018 13:42:17 -0700 Subject: [PATCH] Remove unnecessary branching from updateContextProvider (#13282) This code had gotten unnecessarily complex after some recent changes. Cleaned it up a bit. --- .../src/ReactFiberBeginWork.js | 84 ++++--------------- .../src/ReactFiberNewContext.js | 41 ++++++++- packages/react/src/ReactContext.js | 3 +- packages/shared/ReactTypes.js | 1 - 4 files changed, 54 insertions(+), 75 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index dcd402072a2851..fb71d185819639 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -73,6 +73,7 @@ import { propagateContextChange, readContext, prepareToReadContext, + calculateChangedBits, } from './ReactFiberNewContext'; import { markActualRenderTimeStarted, @@ -824,88 +825,34 @@ function updateContextProvider(current, workInProgress, renderExpirationTime) { // Initial render changedBits = MAX_SIGNED_31_BIT_INT; } else { - if (oldProps.value === newProps.value) { + const oldValue = oldProps.value; + changedBits = calculateChangedBits(context, newValue, oldValue); + if (changedBits === 0) { // No change. Bailout early if children are the same. if ( oldProps.children === newProps.children && !hasLegacyContextChanged() ) { - workInProgress.stateNode = 0; - pushProvider(workInProgress); + pushProvider(workInProgress, 0); return bailoutOnAlreadyFinishedWork( current, workInProgress, renderExpirationTime, ); } - changedBits = 0; } else { - const oldValue = oldProps.value; - // Use Object.is to compare the new context value to the old value. - // Inlined Object.is polyfill. - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is - if ( - (oldValue === newValue && - (oldValue !== 0 || 1 / oldValue === 1 / newValue)) || - (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare - ) { - // No change. Bailout early if children are the same. - if ( - oldProps.children === newProps.children && - !hasLegacyContextChanged() - ) { - workInProgress.stateNode = 0; - pushProvider(workInProgress); - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderExpirationTime, - ); - } - changedBits = 0; - } else { - changedBits = - typeof context._calculateChangedBits === 'function' - ? context._calculateChangedBits(oldValue, newValue) - : MAX_SIGNED_31_BIT_INT; - if (__DEV__) { - warning( - (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits, - 'calculateChangedBits: Expected the return value to be a ' + - '31-bit integer. Instead received: %s', - changedBits, - ); - } - changedBits |= 0; - - if (changedBits === 0) { - // No change. Bailout early if children are the same. - if ( - oldProps.children === newProps.children && - !hasLegacyContextChanged() - ) { - workInProgress.stateNode = 0; - pushProvider(workInProgress); - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderExpirationTime, - ); - } - } else { - propagateContextChange( - workInProgress, - context, - changedBits, - renderExpirationTime, - ); - } - } + // The context value changed. Search for matching consumers and schedule + // them to update. + propagateContextChange( + workInProgress, + context, + changedBits, + renderExpirationTime, + ); } } - workInProgress.stateNode = changedBits; - pushProvider(workInProgress); + pushProvider(workInProgress, changedBits); const newChildren = newProps.children; reconcileChildren(current, workInProgress, newChildren, renderExpirationTime); @@ -1049,8 +996,7 @@ function beginWork( ); break; case ContextProvider: - workInProgress.stateNode = 0; - pushProvider(workInProgress); + pushProvider(workInProgress, 0); break; case Profiler: if (enableProfilerTimer) { diff --git a/packages/react-reconciler/src/ReactFiberNewContext.js b/packages/react-reconciler/src/ReactFiberNewContext.js index 91d0b52826edf2..e8f8e1b1e22e3d 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.js @@ -26,7 +26,9 @@ import {NoWork} from './ReactFiberExpirationTime'; import {ContextProvider} from 'shared/ReactTypeOfWork'; import invariant from 'shared/invariant'; +import warning from 'shared/warning'; +import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt'; const valueCursor: StackCursor = createCursor(null); const changedBitsCursor: StackCursor = createCursor(0); @@ -48,7 +50,7 @@ export function resetContextDependences(): void { lastContextWithAllBitsObserved = null; } -export function pushProvider(providerFiber: Fiber): void { +export function pushProvider(providerFiber: Fiber, changedBits: number): void { const context: ReactContext = providerFiber.type._context; if (isPrimaryRenderer) { @@ -56,7 +58,7 @@ export function pushProvider(providerFiber: Fiber): void { push(valueCursor, context._currentValue, providerFiber); context._currentValue = providerFiber.pendingProps.value; - context._changedBits = providerFiber.stateNode; + context._changedBits = changedBits; if (__DEV__) { warningWithoutStack( context._currentRenderer === undefined || @@ -72,7 +74,7 @@ export function pushProvider(providerFiber: Fiber): void { push(valueCursor, context._currentValue2, providerFiber); context._currentValue2 = providerFiber.pendingProps.value; - context._changedBits2 = providerFiber.stateNode; + context._changedBits2 = changedBits; if (__DEV__) { warningWithoutStack( context._currentRenderer2 === undefined || @@ -103,6 +105,39 @@ export function popProvider(providerFiber: Fiber): void { } } +export function calculateChangedBits( + context: ReactContext, + newValue: T, + oldValue: T, +) { + // Use Object.is to compare the new context value to the old value. Inlined + // Object.is polyfill. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is + if ( + (oldValue === newValue && + (oldValue !== 0 || 1 / oldValue === 1 / (newValue: any))) || + (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare + ) { + // No change + return 0; + } else { + const changedBits = + typeof context._calculateChangedBits === 'function' + ? context._calculateChangedBits(oldValue, newValue) + : MAX_SIGNED_31_BIT_INT; + + if (__DEV__) { + warning( + (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits, + 'calculateChangedBits: Expected the return value to be a ' + + '31-bit integer. Instead received: %s', + changedBits, + ); + } + return changedBits | 0; + } +} + export function propagateContextChange( workInProgress: Fiber, context: ReactContext, diff --git a/packages/react/src/ReactContext.js b/packages/react/src/ReactContext.js index 02ea7959af5354..bdbb55f9389fd3 100644 --- a/packages/react/src/ReactContext.js +++ b/packages/react/src/ReactContext.js @@ -50,13 +50,12 @@ export function createContext( const context: ReactContext = { $$typeof: REACT_CONTEXT_TYPE, _calculateChangedBits: calculateChangedBits, - _defaultValue: defaultValue, - _currentValue: defaultValue, // As a workaround to support multiple concurrent renderers, we categorize // some renderers as primary and others as secondary. We only expect // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. + _currentValue: defaultValue, _currentValue2: defaultValue, _changedBits: 0, _changedBits2: 0, diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js index bff9413c828549..c662a285aa01f8 100644 --- a/packages/shared/ReactTypes.js +++ b/packages/shared/ReactTypes.js @@ -82,7 +82,6 @@ export type ReactContext = { unstable_read: () => T, _calculateChangedBits: ((a: T, b: T) => number) | null, - _defaultValue: T, _currentValue: T, _currentValue2: T,