Interleaved Context.Provider bugfix (#12187)
* Added failing unit test * Maybe fixed interleaved context provider bug?
Brian Vaughn committed
Feb 8, 2018 at 14:23 UTC
b5e961508709ac61f7e2de60a047238216f09a04
2 files changed
+47
-9
packages/react-reconciler/src/ReactFiberNewContext.js
+13
-9
@@ -12,6 +12,8 @@ import type {ReactContext} from 'shared/ReactTypes';
12
13
import warning from 'fbjs/lib/warning';
14
15
+let changedBitsStack: Array<any> = [];
16
+let currentValueStack: Array<any> = [];
17
let stack: Array<Fiber> = [];
18
let index = -1;
19
@@ -22,9 +24,11 @@ if (__DEV__) {
24
}
25
26
export function pushProvider(providerFiber: Fiber): void {
27
+ const context: ReactContext<any> = providerFiber.type.context;
28
index += 1;
29
+ changedBitsStack[index] = context.changedBits;
30
+ currentValueStack[index] = context.currentValue;
31
stack[index] = providerFiber;
27
- const context: ReactContext<any> = providerFiber.type.context;
32
context.currentValue = providerFiber.pendingProps.value;
33
context.changedBits = providerFiber.stateNode;
34
@@ -43,17 +47,15 @@ export function popProvider(providerFiber: Fiber): void {
47
if (__DEV__) {
48
warning(index > -1 && providerFiber === stack[index], 'Unexpected pop.');
49
}
50
+ const changedBits = changedBitsStack[index];
51
+ const currentValue = currentValueStack[index];
52
+ changedBitsStack[index] = null;
53
+ currentValueStack[index] = null;
54
stack[index] = null;
55
index -= 1;
56
const context: ReactContext<any> = providerFiber.type.context;
49
- if (index < 0) {
50
- context.currentValue = context.defaultValue;
51
- context.changedBits = 0;
52
- } else {
53
- const previousProviderFiber = stack[index];
54
- context.currentValue = previousProviderFiber.pendingProps.value;
55
- context.changedBits = previousProviderFiber.stateNode;
56
- }
57
+ context.currentValue = currentValue;
58
+ context.changedBits = changedBits;
59
}
60
61
export function resetProviderStack(): void {
@@ -62,6 +64,8 @@ export function resetProviderStack(): void {
64
const context: ReactContext<any> = providerFiber.type.context;
65
context.currentValue = context.defaultValue;
66
context.changedBits = 0;
67
+ changedBitsStack[i] = null;
68
+ currentValueStack[i] = null;
69
stack[i] = null;
70
if (__DEV__) {
71
context._currentRenderer = null;
packages/react-reconciler/src/__tests__/ReactNewContext-test.internal.js
+34
@@ -266,6 +266,40 @@ describe('ReactNewContext', () => {
266
expect(ReactNoop.getChildren()).toEqual([span('Result: 12')]);
267
});
268
269
+ it('should provide the correct (default) values to consumers outside of a provider', () => {
270
+ const FooContext = React.createContext({value: 'foo-initial'});
271
+ const BarContext = React.createContext({value: 'bar-initial'});
272
+
273
+ const Verify = ({actual, expected}) => {
274
+ expect(expected).toBe(actual);
275
+ return null;
276
+ };
277
+
278
+ ReactNoop.render(
279
+ <React.Fragment>
280
+ <BarContext.Provider value={{value: 'bar-updated'}}>
281
+ <BarContext.Consumer>
282
+ {({value}) => <Verify actual={value} expected="bar-updated" />}
283
+ </BarContext.Consumer>
284
+
285
+ <FooContext.Provider value={{value: 'foo-updated'}}>
286
+ <FooContext.Consumer>
287
+ {({value}) => <Verify actual={value} expected="foo-updated" />}
288
+ </FooContext.Consumer>
289
+ </FooContext.Provider>
290
+ </BarContext.Provider>
291
+
292
+ <FooContext.Consumer>
293
+ {({value}) => <Verify actual={value} expected="foo-initial" />}
294
+ </FooContext.Consumer>
295
+ <BarContext.Consumer>
296
+ {({value}) => <Verify actual={value} expected="bar-initial" />}
297
+ </BarContext.Consumer>
298
+ </React.Fragment>,
299
+ );
300
+ ReactNoop.flush();
301
+ });
302
+
303
it('multiple consumers in different branches', () => {
304
const Context = React.createContext(1);
305