Skip to content

Commit 23be410

Browse files
ericsoderberghpgaearon
authored andcommitted
Fixed an issue with nested contexts unwinding when server rendering. 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
1 parent d0d4280 commit 23be410

File tree

2 files changed

+74
-7
lines changed

2 files changed

+74
-7
lines changed

packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,5 +191,63 @@ describe('ReactDOMServerIntegration', () => {
191191
expect(e.querySelector('#theme').textContent).toBe('light');
192192
expect(e.querySelector('#language').textContent).toBe('english');
193193
});
194+
195+
itRenders('nested context unwinding', async render => {
196+
const Theme = React.createContext('dark');
197+
const Language = React.createContext('french');
198+
199+
const App = () => (
200+
<div>
201+
<Theme.Provider value="light">
202+
<Language.Provider value="english">
203+
<Theme.Provider value="dark">
204+
<Theme.Consumer>
205+
{theme => <div id="theme1">{theme}</div>}
206+
</Theme.Consumer>
207+
</Theme.Provider>
208+
<Theme.Consumer>
209+
{theme => <div id="theme2">{theme}</div>}
210+
</Theme.Consumer>
211+
<Language.Provider value="sanskrit">
212+
<Theme.Provider value="blue">
213+
<Theme.Provider value="red">
214+
<Language.Consumer>
215+
{() => (
216+
<Language.Provider value="chinese">
217+
<Language.Provider value="hungarian" />
218+
<Language.Consumer>
219+
{language => <div id="language1">{language}</div>}
220+
</Language.Consumer>
221+
</Language.Provider>
222+
)}
223+
</Language.Consumer>
224+
</Theme.Provider>
225+
<Language.Consumer>
226+
{language => (
227+
<React.Fragment>
228+
<Theme.Consumer>
229+
{theme => <div id="theme3">{theme}</div>}
230+
</Theme.Consumer>
231+
<div id="language2">{language}</div>
232+
</React.Fragment>
233+
)}
234+
</Language.Consumer>
235+
</Theme.Provider>
236+
</Language.Provider>
237+
</Language.Provider>
238+
</Theme.Provider>
239+
<Language.Consumer>
240+
{language => <div id="language3">{language}</div>}
241+
</Language.Consumer>
242+
</div>
243+
);
244+
let e = await render(<App />);
245+
expect(e.querySelector('#theme1').textContent).toBe('dark');
246+
expect(e.querySelector('#theme2').textContent).toBe('light');
247+
expect(e.querySelector('#theme3').textContent).toBe('blue');
248+
expect(e.querySelector('#language1').textContent).toBe('chinese');
249+
expect(e.querySelector('#language2').textContent).toBe('sanskrit');
250+
expect(e.querySelector('#language3').textContent).toBe('french');
251+
});
194252
});
195253
});

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -689,14 +689,23 @@ class ReactDOMServerRenderer {
689689
this.providerStack[this.providerIndex] = null;
690690
this.providerIndex -= 1;
691691
const context: ReactContext<any> = provider.type._context;
692-
if (this.providerIndex < 0) {
693-
context._currentValue = context._defaultValue;
692+
693+
// Find the closest parent provider of the same type and use its value.
694+
// TODO: it would be nice to avoid this being O(N).
695+
let contextPriorProvider = null;
696+
for (let i = this.providerIndex; i >= 0; i--) {
697+
// We assume this Flow type is correct because of the index check above
698+
// and because pushProvider() enforces the correct type.
699+
const priorProvider: ReactProvider<any> = (this.providerStack[i]: any);
700+
if (priorProvider.type === provider.type) {
701+
contextPriorProvider = priorProvider;
702+
break;
703+
}
704+
}
705+
if (contextPriorProvider !== null) {
706+
context._currentValue = contextPriorProvider.props.value;
694707
} else {
695-
// We assume this type is correct because of the index check above.
696-
const previousProvider: ReactProvider<any> = (this.providerStack[
697-
this.providerIndex
698-
]: any);
699-
context._currentValue = previousProvider.props.value;
708+
context._currentValue = context._defaultValue;
700709
}
701710
}
702711

0 commit comments

Comments
 (0)