@samitouri / QOS-React / commits / fa65c58e15

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(