Skip to content

Commit

Permalink
Remove unnecessary branching from updateContextProvider (#13282)
Browse files Browse the repository at this point in the history
This code had gotten unnecessarily complex after some recent changes.
Cleaned it up a bit.
  • Loading branch information
acdlite committed Jul 27, 2018
1 parent 840cb1a commit 2a2ef7e
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 75 deletions.
84 changes: 15 additions & 69 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import {
propagateContextChange,
readContext,
prepareToReadContext,
calculateChangedBits,
} from './ReactFiberNewContext';
import {
markActualRenderTimeStarted,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1049,8 +996,7 @@ function beginWork(
);
break;
case ContextProvider:
workInProgress.stateNode = 0;
pushProvider(workInProgress);
pushProvider(workInProgress, 0);
break;
case Profiler:
if (enableProfilerTimer) {
Expand Down
41 changes: 38 additions & 3 deletions packages/react-reconciler/src/ReactFiberNewContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<mixed> = createCursor(null);
const changedBitsCursor: StackCursor<number> = createCursor(0);

Expand All @@ -48,15 +50,15 @@ export function resetContextDependences(): void {
lastContextWithAllBitsObserved = null;
}

export function pushProvider(providerFiber: Fiber): void {
export function pushProvider(providerFiber: Fiber, changedBits: number): void {
const context: ReactContext<any> = providerFiber.type._context;

if (isPrimaryRenderer) {
push(changedBitsCursor, context._changedBits, providerFiber);
push(valueCursor, context._currentValue, providerFiber);

context._currentValue = providerFiber.pendingProps.value;
context._changedBits = providerFiber.stateNode;
context._changedBits = changedBits;
if (__DEV__) {
warningWithoutStack(
context._currentRenderer === undefined ||
Expand All @@ -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 ||
Expand Down Expand Up @@ -103,6 +105,39 @@ export function popProvider(providerFiber: Fiber): void {
}
}

export function calculateChangedBits<T>(
context: ReactContext<T>,
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<mixed>,
Expand Down
3 changes: 1 addition & 2 deletions packages/react/src/ReactContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,12 @@ export function createContext<T>(
const context: ReactContext<T> = {
$$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,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/ReactTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ export type ReactContext<T> = {
unstable_read: () => T,

_calculateChangedBits: ((a: T, b: T) => number) | null,
_defaultValue: T,

_currentValue: T,
_currentValue2: T,
Expand Down

0 comments on commit 2a2ef7e

Please sign in to comment.