Add readContext to SSR (#13888)
Will be used by react-cache.
Sebastian Markbåge committed
Oct 18, 2018 at 20:20 UTC
fa65c58e15ca6b718a9771782e77d8a990a011d7
2 files changed
+94
-37
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+42
@@ -13,6 +13,7 @@
13
let React;
14
let ReactDOMServer;
15
let PropTypes;
16
+let ReactCurrentOwner;
17
18
function normalizeCodeLocInfo(str) {
19
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -24,6 +25,9 @@ describe('ReactDOMServer', () => {
25
React = require('react');
26
PropTypes = require('prop-types');
27
ReactDOMServer = require('react-dom/server');
28
+ ReactCurrentOwner =
29
+ React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
30
+ .ReactCurrentOwner;
31
});
32
33
describe('renderToString', () => {
@@ -431,6 +435,44 @@ describe('ReactDOMServer', () => {
435
expect(results).toEqual([2, 1, 3, 1]);
436
});
437
438
+ it('renders with dispatcher.readContext mechanism', () => {
439
+ const Context = React.createContext(0);
440
+
441
+ function readContext(context) {
442
+ return ReactCurrentOwner.currentDispatcher.readContext(context);
443
+ }
444
+
445
+ function Consumer(props) {
446
+ return 'Result: ' + readContext(Context);
447
+ }
448
+
449
+ const Indirection = React.Fragment;
450
+
451
+ function App(props) {
452
+ return (
453
+ <Context.Provider value={props.value}>
454
+ <Context.Provider value={2}>
455
+ <Consumer />
456
+ </Context.Provider>
457
+ <Indirection>
458
+ <Indirection>
459
+ <Consumer />
460
+ <Context.Provider value={3}>
461
+ <Consumer />
462
+ </Context.Provider>
463
+ </Indirection>
464
+ </Indirection>
465
+ <Consumer />
466
+ </Context.Provider>
467
+ );
468
+ }
469
+
470
+ const markup = ReactDOMServer.renderToString(<App value={1} />);
471
+ // Extract the numbers rendered by the consumers
472
+ const results = markup.match(/\d+/g).map(Number);
473
+ expect(results).toEqual([2, 1, 3, 1]);
474
+ });
475
+
476
it('renders context API, reentrancy', () => {
477
const Context = React.createContext(0);
478
packages/react-dom/src/server/ReactPartialRenderer.js
+52
-37
@@ -75,6 +75,7 @@ const toArray = ((React.Children.toArray: any): toArrayType);
75
// Each stack is an array of frames which may contain nested stacks of elements.
76
let currentDebugStacks = [];
77
78
+let ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
79
let ReactDebugCurrentFrame;
80
let prevGetCurrentStackImpl = null;
81
let getCurrentServerStackImpl = () => '';
@@ -85,6 +86,15 @@ let pushCurrentDebugStack = (stack: Array<Frame>) => {};
86
let pushElementToDebugStack = (element: ReactElement) => {};
87
let popCurrentDebugStack = () => {};
88
89
+let Dispatcher = {
90
+ readContext<T>(
91
+ context: ReactContext<T>,
92
+ observedBits: void | number | boolean,
93
+ ): T {
94
+ return context._currentValue;
95
+ },
96
+};
97
+
98
if (__DEV__) {
99
ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
100
@@ -787,49 +797,54 @@ class ReactDOMServerRenderer {
797
return null;
798
}
799
790
- let out = '';
791
- while (out.length < bytes) {
792
- if (this.stack.length === 0) {
793
- this.exhausted = true;
794
- break;
795
- }
796
- const frame: Frame = this.stack[this.stack.length - 1];
797
- if (frame.childIndex >= frame.children.length) {
798
- const footer = frame.footer;
799
- out += footer;
800
- if (footer !== '') {
801
- this.previousWasTextNode = false;
800
+ ReactCurrentOwner.currentDispatcher = Dispatcher;
801
+ try {
802
+ let out = '';
803
+ while (out.length < bytes) {
804
+ if (this.stack.length === 0) {
805
+ this.exhausted = true;
806
+ break;
807
}
803
- this.stack.pop();
804
- if (frame.type === 'select') {
805
- this.currentSelectValue = null;
806
- } else if (
807
- frame.type != null &&
808
- frame.type.type != null &&
809
- frame.type.type.$$typeof === REACT_PROVIDER_TYPE
810
- ) {
811
- const provider: ReactProvider<any> = (frame.type: any);
812
- this.popProvider(provider);
808
+ const frame: Frame = this.stack[this.stack.length - 1];
809
+ if (frame.childIndex >= frame.children.length) {
810
+ const footer = frame.footer;
811
+ out += footer;
812
+ if (footer !== '') {
813
+ this.previousWasTextNode = false;
814
+ }
815
+ this.stack.pop();
816
+ if (frame.type === 'select') {
817
+ this.currentSelectValue = null;
818
+ } else if (
819
+ frame.type != null &&
820
+ frame.type.type != null &&
821
+ frame.type.type.$$typeof === REACT_PROVIDER_TYPE
822
+ ) {
823
+ const provider: ReactProvider<any> = (frame.type: any);
824
+ this.popProvider(provider);
825
+ }
826
+ continue;
827
}
814
- continue;
815
- }
816
- const child = frame.children[frame.childIndex++];
817
- if (__DEV__) {
818
- pushCurrentDebugStack(this.stack);
819
- // We're starting work on this frame, so reset its inner stack.
820
- ((frame: any): FrameDev).debugElementStack.length = 0;
821
- try {
822
- // Be careful! Make sure this matches the PROD path below.
828
+ const child = frame.children[frame.childIndex++];
829
+ if (__DEV__) {
830
+ pushCurrentDebugStack(this.stack);
831
+ // We're starting work on this frame, so reset its inner stack.
832
+ ((frame: any): FrameDev).debugElementStack.length = 0;
833
+ try {
834
+ // Be careful! Make sure this matches the PROD path below.
835
+ out += this.render(child, frame.context, frame.domNamespace);
836
+ } finally {
837
+ popCurrentDebugStack();
838
+ }
839
+ } else {
840
+ // Be careful! Make sure this matches the DEV path above.
841
out += this.render(child, frame.context, frame.domNamespace);
824
- } finally {
825
- popCurrentDebugStack();
842
}
827
- } else {
828
- // Be careful! Make sure this matches the DEV path above.
829
- out += this.render(child, frame.context, frame.domNamespace);
843
}
844
+ return out;
845
+ } finally {
846
+ ReactCurrentOwner.currentDispatcher = null;
847
}
832
- return out;
848
}
849
850
render(