Skip to content

Commit

Permalink
Fixed an issue with nested contexts unwinding when server rendering. …
Browse files Browse the repository at this point in the history
…Issue #12984 (#12985)

* Fixed an issue with nested contexts unwinding when server rendering. GitHub issue #12984

* Fixed an issue with search direction and stricter false checking

* Use decrement infix operator

* Streamlined existence checks

* Streamlined assignment. Removed redundant comment. Use null for array values

* Made prettier

* Relaxed type checking and improved comment

* Improve test coverage
  • Loading branch information
ericsoderberghp authored and gaearon committed Jun 11, 2018
1 parent d0d4280 commit 23be410
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 7 deletions.
Expand Up @@ -191,5 +191,63 @@ 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 value="english">
<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 value="sanskrit">
<Theme.Provider value="blue">
<Theme.Provider value="red">
<Language.Consumer>
{() => (
<Language.Provider value="chinese">
<Language.Provider value="hungarian" />
<Language.Consumer>
{language => <div id="language1">{language}</div>}
</Language.Consumer>
</Language.Provider>
)}
</Language.Consumer>
</Theme.Provider>
<Language.Consumer>
{language => (
<React.Fragment>
<Theme.Consumer>
{theme => <div id="theme3">{theme}</div>}
</Theme.Consumer>
<div id="language2">{language}</div>
</React.Fragment>
)}
</Language.Consumer>
</Theme.Provider>
</Language.Provider>
</Language.Provider>
</Theme.Provider>
<Language.Consumer>
{language => <div id="language3">{language}</div>}
</Language.Consumer>
</div>
);
let e = await render(<App />);
expect(e.querySelector('#theme1').textContent).toBe('dark');
expect(e.querySelector('#theme2').textContent).toBe('light');
expect(e.querySelector('#theme3').textContent).toBe('blue');
expect(e.querySelector('#language1').textContent).toBe('chinese');
expect(e.querySelector('#language2').textContent).toBe('sanskrit');
expect(e.querySelector('#language3').textContent).toBe('french');
});
});
});
23 changes: 16 additions & 7 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Expand Up @@ -689,14 +689,23 @@ class ReactDOMServerRenderer {
this.providerStack[this.providerIndex] = null;
this.providerIndex -= 1;
const context: ReactContext<any> = provider.type._context;
if (this.providerIndex < 0) {
context._currentValue = context._defaultValue;

// Find the closest parent provider of the same type and use its value.
// TODO: it would be nice to avoid this being O(N).
let contextPriorProvider = null;
for (let i = this.providerIndex; i >= 0; i--) {
// We assume this Flow type is correct because of the index check above
// and because pushProvider() enforces the correct type.
const priorProvider: ReactProvider<any> = (this.providerStack[i]: any);
if (priorProvider.type === provider.type) {
contextPriorProvider = priorProvider;
break;
}
}
if (contextPriorProvider !== null) {
context._currentValue = contextPriorProvider.props.value;
} else {
// We assume this type is correct because of the index check above.
const previousProvider: ReactProvider<any> = (this.providerStack[
this.providerIndex
]: any);
context._currentValue = previousProvider.props.value;
context._currentValue = context._defaultValue;
}
}

Expand Down

0 comments on commit 23be410

Please sign in to comment.