Skip to content

Commit

Permalink
Don't bail on new context Provider if a legacy provider rendered above (
Browse files Browse the repository at this point in the history
#12586)

* Don't bail on new context Provider if a legacy provider rendered above

* Avoid an extra variable
  • Loading branch information
gaearon committed Apr 26, 2018
1 parent d883d59 commit 7c39328
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 3 deletions.
8 changes: 5 additions & 3 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -859,8 +859,10 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(

const newProps = workInProgress.pendingProps;
const oldProps = workInProgress.memoizedProps;
let canBailOnProps = true;

if (hasLegacyContextChanged()) {
canBailOnProps = false;
// Normally we can bail out on props equality but if context has changed
// we don't do the bailout and we have to reuse existing props instead.
} else if (oldProps === newProps) {
Expand Down Expand Up @@ -893,7 +895,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
} else {
if (oldProps.value === newProps.value) {
// No change. Bailout early if children are the same.
if (oldProps.children === newProps.children) {
if (oldProps.children === newProps.children && canBailOnProps) {
workInProgress.stateNode = 0;
pushProvider(workInProgress);
return bailoutOnAlreadyFinishedWork(current, workInProgress);
Expand All @@ -910,7 +912,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
(oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
) {
// No change. Bailout early if children are the same.
if (oldProps.children === newProps.children) {
if (oldProps.children === newProps.children && canBailOnProps) {
workInProgress.stateNode = 0;
pushProvider(workInProgress);
return bailoutOnAlreadyFinishedWork(current, workInProgress);
Expand All @@ -933,7 +935,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(

if (changedBits === 0) {
// No change. Bailout early if children are the same.
if (oldProps.children === newProps.children) {
if (oldProps.children === newProps.children && canBailOnProps) {
workInProgress.stateNode = 0;
pushProvider(workInProgress);
return bailoutOnAlreadyFinishedWork(current, workInProgress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,72 @@ describe('ReactNewContext', () => {
expect(ReactNoop.getChildren()).toEqual([span('Child')]);
});

it('provider does not bail out if legacy context changed above', () => {
const Context = React.createContext(0);

function Child() {
ReactNoop.yield('Child');
return <span prop="Child" />;
}

const children = <Child />;

class LegacyProvider extends React.Component {
static childContextTypes = {
legacyValue: () => {},
};
state = {legacyValue: 1};
getChildContext() {
return {legacyValue: this.state.legacyValue};
}
render() {
ReactNoop.yield('LegacyProvider');
return this.props.children;
}
}

class App extends React.Component {
state = {value: 1};
render() {
ReactNoop.yield('App');
return (
<Context.Provider value={this.state.value}>
{this.props.children}
</Context.Provider>
);
}
}

const legacyProviderRef = React.createRef();
const appRef = React.createRef();

// Initial mount
ReactNoop.render(
<LegacyProvider ref={legacyProviderRef}>
<App ref={appRef} value={1}>
{children}
</App>
</LegacyProvider>,
);
expect(ReactNoop.flush()).toEqual(['LegacyProvider', 'App', 'Child']);
expect(ReactNoop.getChildren()).toEqual([span('Child')]);

// Update App with same value (should bail out)
appRef.current.setState({value: 1});
expect(ReactNoop.flush()).toEqual(['App']);
expect(ReactNoop.getChildren()).toEqual([span('Child')]);

// Update LegacyProvider (should not bail out)
legacyProviderRef.current.setState({value: 1});
expect(ReactNoop.flush()).toEqual(['LegacyProvider', 'App', 'Child']);
expect(ReactNoop.getChildren()).toEqual([span('Child')]);

// Update App with same value (should bail out)
appRef.current.setState({value: 1});
expect(ReactNoop.flush()).toEqual(['App']);
expect(ReactNoop.getChildren()).toEqual([span('Child')]);
});

it('consumer bails out if value is unchanged and something above bailed out', () => {
const Context = React.createContext(0);

Expand Down

0 comments on commit 7c39328

Please sign in to comment.