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
Eric Soderberg committed
Jun 11, 2018 at 06:25 UTC
23be4102df273d6ee6d9be42c6d65885d0a914b1
2 files changed
+74
-7
packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js
+58
@@ -191,5 +191,63 @@ describe('ReactDOMServerIntegration', () => {
191
expect(e.querySelector('#theme').textContent).toBe('light');
192
expect(e.querySelector('#language').textContent).toBe('english');
193
});
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
+ });
252
});
253
});
packages/react-dom/src/server/ReactPartialRenderer.js
+16
-7
@@ -689,14 +689,23 @@ class ReactDOMServerRenderer {
689
this.providerStack[this.providerIndex] = null;
690
this.providerIndex -= 1;
691
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;
707
} 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;
709
}
710
}
711