Interleaved Context.Provider bugfix (#12187)

* Added failing unit test

* Maybe fixed interleaved context provider bug?
This commit is contained in:
Brian Vaughn 2018-02-08 14:23:41 -08:00 committed by GitHub
parent 49b0ca1b83
commit b5e9615087
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 10 deletions

View File

@ -12,6 +12,8 @@ import type {ReactContext} from 'shared/ReactTypes';
import warning from 'fbjs/lib/warning';
let changedBitsStack: Array<any> = [];
let currentValueStack: Array<any> = [];
let stack: Array<Fiber> = [];
let index = -1;
@ -22,9 +24,11 @@ if (__DEV__) {
}
export function pushProvider(providerFiber: Fiber): void {
index += 1;
stack[index] = providerFiber;
const context: ReactContext<any> = providerFiber.type.context;
index += 1;
changedBitsStack[index] = context.changedBits;
currentValueStack[index] = context.currentValue;
stack[index] = providerFiber;
context.currentValue = providerFiber.pendingProps.value;
context.changedBits = providerFiber.stateNode;
@ -43,17 +47,15 @@ export function popProvider(providerFiber: Fiber): void {
if (__DEV__) {
warning(index > -1 && providerFiber === stack[index], 'Unexpected pop.');
}
const changedBits = changedBitsStack[index];
const currentValue = currentValueStack[index];
changedBitsStack[index] = null;
currentValueStack[index] = null;
stack[index] = null;
index -= 1;
const context: ReactContext<any> = providerFiber.type.context;
if (index < 0) {
context.currentValue = context.defaultValue;
context.changedBits = 0;
} else {
const previousProviderFiber = stack[index];
context.currentValue = previousProviderFiber.pendingProps.value;
context.changedBits = previousProviderFiber.stateNode;
}
context.currentValue = currentValue;
context.changedBits = changedBits;
}
export function resetProviderStack(): void {
@ -62,6 +64,8 @@ export function resetProviderStack(): void {
const context: ReactContext<any> = providerFiber.type.context;
context.currentValue = context.defaultValue;
context.changedBits = 0;
changedBitsStack[i] = null;
currentValueStack[i] = null;
stack[i] = null;
if (__DEV__) {
context._currentRenderer = null;

View File

@ -266,6 +266,40 @@ describe('ReactNewContext', () => {
expect(ReactNoop.getChildren()).toEqual([span('Result: 12')]);
});
it('should provide the correct (default) values to consumers outside of a provider', () => {
const FooContext = React.createContext({value: 'foo-initial'});
const BarContext = React.createContext({value: 'bar-initial'});
const Verify = ({actual, expected}) => {
expect(expected).toBe(actual);
return null;
};
ReactNoop.render(
<React.Fragment>
<BarContext.Provider value={{value: 'bar-updated'}}>
<BarContext.Consumer>
{({value}) => <Verify actual={value} expected="bar-updated" />}
</BarContext.Consumer>
<FooContext.Provider value={{value: 'foo-updated'}}>
<FooContext.Consumer>
{({value}) => <Verify actual={value} expected="foo-updated" />}
</FooContext.Consumer>
</FooContext.Provider>
</BarContext.Provider>
<FooContext.Consumer>
{({value}) => <Verify actual={value} expected="foo-initial" />}
</FooContext.Consumer>
<BarContext.Consumer>
{({value}) => <Verify actual={value} expected="bar-initial" />}
</BarContext.Consumer>
</React.Fragment>,
);
ReactNoop.flush();
});
it('multiple consumers in different branches', () => {
const Context = React.createContext(1);