Add readContext to SSR (#13888)

Will be used by react-cache.
This commit is contained in:
Sebastian Markbåge
2018-10-18 20:20:03 -07:00
committed by GitHub
parent d9a3cc070c
commit fa65c58e15
2 changed files with 94 additions and 37 deletions
@@ -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 (
<Context.Provider value={props.value}>
<Context.Provider value={2}>
<Consumer />
</Context.Provider>
<Indirection>
<Indirection>
<Consumer />
<Context.Provider value={3}>
<Consumer />
</Context.Provider>
</Indirection>
</Indirection>
<Consumer />
</Context.Provider>
);
}
const markup = ReactDOMServer.renderToString(<App value={1} />);
// 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);
+52 -37
View File
@@ -75,6 +75,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 = () => '';
@@ -85,6 +86,15 @@ let pushCurrentDebugStack = (stack: Array<Frame>) => {};
let pushElementToDebugStack = (element: ReactElement) => {};
let popCurrentDebugStack = () => {};
let Dispatcher = {
readContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
return context._currentValue;
},
};
if (__DEV__) {
ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
@@ -787,49 +797,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<any> = (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<any> = (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(