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,