@samitouri / QOS-React / commits / b5e9615087

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