diff --git a/packages/react-dom/src/__tests__/ReactServerRendering-test.js b/packages/react-dom/src/__tests__/ReactServerRendering-test.js
index 27b30f9bb03e2..371a688ee5163 100644
--- a/packages/react-dom/src/__tests__/ReactServerRendering-test.js
+++ b/packages/react-dom/src/__tests__/ReactServerRendering-test.js
@@ -13,6 +13,7 @@
let React;
let ReactDOMServer;
let PropTypes;
+let ReactCurrentOwner;
function normalizeCodeLocInfo(str) {
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -24,6 +25,9 @@ describe('ReactDOMServer', () => {
React = require('react');
PropTypes = require('prop-types');
ReactDOMServer = require('react-dom/server');
+ ReactCurrentOwner =
+ React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
+ .ReactCurrentOwner;
});
describe('renderToString', () => {
@@ -431,6 +435,44 @@ describe('ReactDOMServer', () => {
expect(results).toEqual([2, 1, 3, 1]);
});
+ it('renders with dispatcher.readContext mechanism', () => {
+ const Context = React.createContext(0);
+
+ function readContext(context) {
+ return ReactCurrentOwner.currentDispatcher.readContext(context);
+ }
+
+ function Consumer(props) {
+ return 'Result: ' + readContext(Context);
+ }
+
+ const Indirection = React.Fragment;
+
+ function App(props) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ const markup = ReactDOMServer.renderToString();
+ // Extract the numbers rendered by the consumers
+ const results = markup.match(/\d+/g).map(Number);
+ expect(results).toEqual([2, 1, 3, 1]);
+ });
+
it('renders context API, reentrancy', () => {
const Context = React.createContext(0);
diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js
index dc79c3fb28031..68ab086cf6e87 100644
--- a/packages/react-dom/src/server/ReactPartialRenderer.js
+++ b/packages/react-dom/src/server/ReactPartialRenderer.js
@@ -74,6 +74,7 @@ const toArray = ((React.Children.toArray: any): toArrayType);
// Each stack is an array of frames which may contain nested stacks of elements.
let currentDebugStacks = [];
+let ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
let ReactDebugCurrentFrame;
let prevGetCurrentStackImpl = null;
let getCurrentServerStackImpl = () => '';
@@ -84,6 +85,15 @@ let pushCurrentDebugStack = (stack: Array) => {};
let pushElementToDebugStack = (element: ReactElement) => {};
let popCurrentDebugStack = () => {};
+let Dispatcher = {
+ readContext(
+ context: ReactContext,
+ observedBits: void | number | boolean,
+ ): T {
+ return context._currentValue;
+ },
+};
+
if (__DEV__) {
ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
@@ -786,49 +796,54 @@ class ReactDOMServerRenderer {
return null;
}
- let out = '';
- while (out.length < bytes) {
- if (this.stack.length === 0) {
- this.exhausted = true;
- break;
- }
- const frame: Frame = this.stack[this.stack.length - 1];
- if (frame.childIndex >= frame.children.length) {
- const footer = frame.footer;
- out += footer;
- if (footer !== '') {
- this.previousWasTextNode = false;
+ ReactCurrentOwner.currentDispatcher = Dispatcher;
+ try {
+ let out = '';
+ while (out.length < bytes) {
+ if (this.stack.length === 0) {
+ this.exhausted = true;
+ break;
}
- this.stack.pop();
- if (frame.type === 'select') {
- this.currentSelectValue = null;
- } else if (
- frame.type != null &&
- frame.type.type != null &&
- frame.type.type.$$typeof === REACT_PROVIDER_TYPE
- ) {
- const provider: ReactProvider = (frame.type: any);
- this.popProvider(provider);
+ const frame: Frame = this.stack[this.stack.length - 1];
+ if (frame.childIndex >= frame.children.length) {
+ const footer = frame.footer;
+ out += footer;
+ if (footer !== '') {
+ this.previousWasTextNode = false;
+ }
+ this.stack.pop();
+ if (frame.type === 'select') {
+ this.currentSelectValue = null;
+ } else if (
+ frame.type != null &&
+ frame.type.type != null &&
+ frame.type.type.$$typeof === REACT_PROVIDER_TYPE
+ ) {
+ const provider: ReactProvider = (frame.type: any);
+ this.popProvider(provider);
+ }
+ continue;
}
- continue;
- }
- const child = frame.children[frame.childIndex++];
- if (__DEV__) {
- pushCurrentDebugStack(this.stack);
- // We're starting work on this frame, so reset its inner stack.
- ((frame: any): FrameDev).debugElementStack.length = 0;
- try {
- // Be careful! Make sure this matches the PROD path below.
+ const child = frame.children[frame.childIndex++];
+ if (__DEV__) {
+ pushCurrentDebugStack(this.stack);
+ // We're starting work on this frame, so reset its inner stack.
+ ((frame: any): FrameDev).debugElementStack.length = 0;
+ try {
+ // Be careful! Make sure this matches the PROD path below.
+ out += this.render(child, frame.context, frame.domNamespace);
+ } finally {
+ popCurrentDebugStack();
+ }
+ } else {
+ // Be careful! Make sure this matches the DEV path above.
out += this.render(child, frame.context, frame.domNamespace);
- } finally {
- popCurrentDebugStack();
}
- } else {
- // Be careful! Make sure this matches the DEV path above.
- out += this.render(child, frame.context, frame.domNamespace);
}
+ return out;
+ } finally {
+ ReactCurrentOwner.currentDispatcher = null;
}
- return out;
}
render(