@samitouri / QOS-React / commits / 2a2ef7e0fd

Remove unnecessary branching from updateContextProvider (#13282)

This code had gotten unnecessarily complex after some recent changes. Cleaned it up a bit.

Andrew Clark committed Jul 27, 2018 at 13:42 UTC 2a2ef7e0fd86612404676b15bb3dd9df7ade536e
4 files changed +54 -75
packages/react-reconciler/src/ReactFiberBeginWork.js
+15 -69
@@ -73,6 +73,7 @@ import {
73 propagateContextChange,
74 readContext,
75 prepareToReadContext,
76 + calculateChangedBits,
77 } from './ReactFiberNewContext';
78 import {
79 markActualRenderTimeStarted,
@@ -824,88 +825,34 @@ function updateContextProvider(current, workInProgress, renderExpirationTime) {
825 // Initial render
826 changedBits = MAX_SIGNED_31_BIT_INT;
827 } else {
827 - if (oldProps.value === newProps.value) {
828 + const oldValue = oldProps.value;
829 + changedBits = calculateChangedBits(context, newValue, oldValue);
830 + if (changedBits === 0) {
831 // No change. Bailout early if children are the same.
832 if (
833 oldProps.children === newProps.children &&
834 !hasLegacyContextChanged()
835 ) {
833 - workInProgress.stateNode = 0;
834 - pushProvider(workInProgress);
836 + pushProvider(workInProgress, 0);
837 return bailoutOnAlreadyFinishedWork(
838 current,
839 workInProgress,
840 renderExpirationTime,
841 );
842 }
841 - changedBits = 0;
843 } else {
843 - const oldValue = oldProps.value;
844 - // Use Object.is to compare the new context value to the old value.
845 - // Inlined Object.is polyfill.
846 - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
847 - if (
848 - (oldValue === newValue &&
849 - (oldValue !== 0 || 1 / oldValue === 1 / newValue)) ||
850 - (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
851 - ) {
852 - // No change. Bailout early if children are the same.
853 - if (
854 - oldProps.children === newProps.children &&
855 - !hasLegacyContextChanged()
856 - ) {
857 - workInProgress.stateNode = 0;
858 - pushProvider(workInProgress);
859 - return bailoutOnAlreadyFinishedWork(
860 - current,
861 - workInProgress,
862 - renderExpirationTime,
863 - );
864 - }
865 - changedBits = 0;
866 - } else {
867 - changedBits =
868 - typeof context._calculateChangedBits === 'function'
869 - ? context._calculateChangedBits(oldValue, newValue)
870 - : MAX_SIGNED_31_BIT_INT;
871 - if (__DEV__) {
872 - warning(
873 - (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
874 - 'calculateChangedBits: Expected the return value to be a ' +
875 - '31-bit integer. Instead received: %s',
876 - changedBits,
877 - );
878 - }
879 - changedBits |= 0;
880 -
881 - if (changedBits === 0) {
882 - // No change. Bailout early if children are the same.
883 - if (
884 - oldProps.children === newProps.children &&
885 - !hasLegacyContextChanged()
886 - ) {
887 - workInProgress.stateNode = 0;
888 - pushProvider(workInProgress);
889 - return bailoutOnAlreadyFinishedWork(
890 - current,
891 - workInProgress,
892 - renderExpirationTime,
893 - );
894 - }
895 - } else {
896 - propagateContextChange(
897 - workInProgress,
898 - context,
899 - changedBits,
900 - renderExpirationTime,
901 - );
902 - }
903 - }
844 + // The context value changed. Search for matching consumers and schedule
845 + // them to update.
846 + propagateContextChange(
847 + workInProgress,
848 + context,
849 + changedBits,
850 + renderExpirationTime,
851 + );
852 }
853 }
854
907 - workInProgress.stateNode = changedBits;
908 - pushProvider(workInProgress);
855 + pushProvider(workInProgress, changedBits);
856
857 const newChildren = newProps.children;
858 reconcileChildren(current, workInProgress, newChildren, renderExpirationTime);
@@ -1049,8 +996,7 @@ function beginWork(
996 );
997 break;
998 case ContextProvider:
1052 - workInProgress.stateNode = 0;
1053 - pushProvider(workInProgress);
999 + pushProvider(workInProgress, 0);
1000 break;
1001 case Profiler:
1002 if (enableProfilerTimer) {
packages/react-reconciler/src/ReactFiberNewContext.js
+38 -3
@@ -26,7 +26,9 @@ import {NoWork} from './ReactFiberExpirationTime';
26 import {ContextProvider} from 'shared/ReactTypeOfWork';
27
28 import invariant from 'shared/invariant';
29 +import warning from 'shared/warning';
30
31 +import MAX_SIGNED_31_BIT_INT from './maxSigned31BitInt';
32 const valueCursor: StackCursor<mixed> = createCursor(null);
33 const changedBitsCursor: StackCursor<number> = createCursor(0);
34
@@ -48,7 +50,7 @@ export function resetContextDependences(): void {
50 lastContextWithAllBitsObserved = null;
51 }
52
51 -export function pushProvider(providerFiber: Fiber): void {
53 +export function pushProvider(providerFiber: Fiber, changedBits: number): void {
54 const context: ReactContext<any> = providerFiber.type._context;
55
56 if (isPrimaryRenderer) {
@@ -56,7 +58,7 @@ export function pushProvider(providerFiber: Fiber): void {
58 push(valueCursor, context._currentValue, providerFiber);
59
60 context._currentValue = providerFiber.pendingProps.value;
59 - context._changedBits = providerFiber.stateNode;
61 + context._changedBits = changedBits;
62 if (__DEV__) {
63 warningWithoutStack(
64 context._currentRenderer === undefined ||
@@ -72,7 +74,7 @@ export function pushProvider(providerFiber: Fiber): void {
74 push(valueCursor, context._currentValue2, providerFiber);
75
76 context._currentValue2 = providerFiber.pendingProps.value;
75 - context._changedBits2 = providerFiber.stateNode;
77 + context._changedBits2 = changedBits;
78 if (__DEV__) {
79 warningWithoutStack(
80 context._currentRenderer2 === undefined ||
@@ -103,6 +105,39 @@ export function popProvider(providerFiber: Fiber): void {
105 }
106 }
107
108 +export function calculateChangedBits<T>(
109 + context: ReactContext<T>,
110 + newValue: T,
111 + oldValue: T,
112 +) {
113 + // Use Object.is to compare the new context value to the old value. Inlined
114 + // Object.is polyfill.
115 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
116 + if (
117 + (oldValue === newValue &&
118 + (oldValue !== 0 || 1 / oldValue === 1 / (newValue: any))) ||
119 + (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
120 + ) {
121 + // No change
122 + return 0;
123 + } else {
124 + const changedBits =
125 + typeof context._calculateChangedBits === 'function'
126 + ? context._calculateChangedBits(oldValue, newValue)
127 + : MAX_SIGNED_31_BIT_INT;
128 +
129 + if (__DEV__) {
130 + warning(
131 + (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
132 + 'calculateChangedBits: Expected the return value to be a ' +
133 + '31-bit integer. Instead received: %s',
134 + changedBits,
135 + );
136 + }
137 + return changedBits | 0;
138 + }
139 +}
140 +
141 export function propagateContextChange(
142 workInProgress: Fiber,
143 context: ReactContext<mixed>,
packages/react/src/ReactContext.js
+1 -2
@@ -50,13 +50,12 @@ export function createContext<T>(
50 const context: ReactContext<T> = {
51 $$typeof: REACT_CONTEXT_TYPE,
52 _calculateChangedBits: calculateChangedBits,
53 - _defaultValue: defaultValue,
54 - _currentValue: defaultValue,
53 // As a workaround to support multiple concurrent renderers, we categorize
54 // some renderers as primary and others as secondary. We only expect
55 // there to be two concurrent renderers at most: React Native (primary) and
56 // Fabric (secondary); React DOM (primary) and React ART (secondary).
57 // Secondary renderers store their context values on separate fields.
58 + _currentValue: defaultValue,
59 _currentValue2: defaultValue,
60 _changedBits: 0,
61 _changedBits2: 0,
packages/shared/ReactTypes.js
-1
@@ -82,7 +82,6 @@ export type ReactContext<T> = {
82 unstable_read: () => T,
83
84 _calculateChangedBits: ((a: T, b: T) => number) | null,
85 - _defaultValue: T,
85
86 _currentValue: T,
87 _currentValue2: T,