diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js index c3c08eb6bf77..2bcece083a4b 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js @@ -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 = () => ( +
+ + + + + {theme =>
{theme}
} +
+
+ + {theme =>
{theme}
} +
+ + + + + {() => ( + + + + {language =>
{language}
} +
+
+ )} +
+
+ + {language => ( + + + {theme =>
{theme}
} +
+
{language}
+
+ )} +
+
+
+
+
+ + {language =>
{language}
} +
+
+ ); + let e = await render(); + 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'); + }); }); }); diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 59ea170b938d..f33ffd239eb2 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -689,14 +689,23 @@ class ReactDOMServerRenderer { this.providerStack[this.providerIndex] = null; this.providerIndex -= 1; const context: ReactContext = 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 = (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 = (this.providerStack[ - this.providerIndex - ]: any); - context._currentValue = previousProvider.props.value; + context._currentValue = context._defaultValue; } }