Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed an issue with nested contexts unwinding when server rendering. Issue #12984 #12985

Merged
merged 8 commits into from Jun 11, 2018
Expand Up @@ -191,5 +191,33 @@ describe('ReactDOMServerIntegration', () => {
expect(e.querySelector('#theme').textContent).toBe('light');
expect(e.querySelector('#language').textContent).toBe('english');
});

itRenders(
'nested context unwinding',
async render => {
const Theme = React.createContext('dark');
const Language = React.createContext('french');

const App = () => (
<div>
<Theme.Provider value="light">
<Language.Provider>
<Theme.Provider value="dark">
<Theme.Consumer>
{theme => <div id="theme1">{theme}</div>}
</Theme.Consumer>
</Theme.Provider>
<Theme.Consumer>
{theme => <div id="theme2">{theme}</div>}
</Theme.Consumer>
</Language.Provider>
</Theme.Provider>
</div>
);
let e = await render(<App />);
expect(e.querySelector('#theme1').textContent).toBe('dark');
expect(e.querySelector('#theme2').textContent).toBe('light');
},
);
});
});
21 changes: 14 additions & 7 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Expand Up @@ -689,14 +689,21 @@ class ReactDOMServerRenderer {
this.providerStack[this.providerIndex] = null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does .length assignment do in V8? I'd like to make sure it doesn't think the array length change is likely to "stay" because it changes all the time. I think just using null is more straightforward and less surprising to the engine (even though the typing isn't as nice for us).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. But, going back to using null seems to require testing for null when accessing array items by index, since Flow has been told that null and undefined are expected array item values. Or, is there an alternate Flow syntax that allows us to remove the explicit checks?

this.providerIndex -= 1;
const context: ReactContext<any> = provider.type._context;
if (this.providerIndex < 0) {
context._currentValue = context._defaultValue;
} else {
// We assume this type is correct because of the index check above.
const previousProvider: ReactProvider<any> = (this.providerStack[
this.providerIndex
]: any);
// find the correct previous provider based on type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is redundant, IMO the code speaks for itself

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

let previousProvider;
if (this.providerIndex > -1) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we can remove this condition? For loop already covers it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

for (let i = 0; i <= this.providerIndex; i += 1) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of searching from the bottom, shouldn't we start search at the top and stop at the first match? Try adding more nesting to the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duh. Fixed.

if (this.providerStack[i] &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please be explicit in type checks (e.g. !== null if that's what you mean). Don't rely on falsiness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I wasn't sure if there was a more Flow appropriate way to check. Flow wanted explicit checks for both null and undefined.

(this.providerStack[i]: ReactProvider<any>).type === provider.type) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was about why we felt comfortable using Flow any:

// We assume this type is correct because of the index check above.

Please keep it above the any case. Maybe change to "Flow type" to disambiguate.

// We assume this Flow type is correct because of the index check above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

previousProvider = this.providerStack[i];
break;
}
}
}
if (previousProvider) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's initialize it to null and then compare to null instead of a truthy check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

context._currentValue = previousProvider.props.value;
} else {
context._currentValue = context._defaultValue;
}
}

Expand Down