@samitouri / QOS-React / commits / 8ecd4bd4f0

Add support for React.pure in ReactDOMServer (#13855)

* Add support for React.pure in ReactDOMServer * Unwrap pure wrappers by creating an additional element as a single child This is very slow but meh. We're rewriting this whole thing anyway.

Alex Taylor committed Oct 20, 2018 at 00:42 UTC 8ecd4bd4f07ea41baa934e6748649e447c999231
2 files changed +94 -1
packages/react-dom/src/__tests__/ReactDOMServerIntegrationSpecialTypes-test.js
+71 -1
@@ -14,6 +14,11 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
14 let React;
15 let ReactDOM;
16 let ReactDOMServer;
17 +let forwardRef;
18 +let pure;
19 +let yieldedValues;
20 +let yieldValue;
21 +let clearYields;
22
23 function initModules() {
24 // Reset warning cache.
@@ -21,6 +26,18 @@ function initModules() {
26 React = require('react');
27 ReactDOM = require('react-dom');
28 ReactDOMServer = require('react-dom/server');
29 + forwardRef = React.forwardRef;
30 + pure = React.pure;
31 +
32 + yieldedValues = [];
33 + yieldValue = value => {
34 + yieldedValues.push(value);
35 + };
36 + clearYields = () => {
37 + const ret = yieldedValues;
38 + yieldedValues = [];
39 + return ret;
40 + };
41
42 // Make them available to the helpers.
43 return {
@@ -40,7 +57,7 @@ describe('ReactDOMServerIntegration', () => {
57 const FunctionComponent = ({label, forwardedRef}) => (
58 <div ref={forwardedRef}>{label}</div>
59 );
43 - const WrappedFunctionComponent = React.forwardRef((props, ref) => (
60 + const WrappedFunctionComponent = forwardRef((props, ref) => (
61 <FunctionComponent {...props} forwardedRef={ref} />
62 ));
63
@@ -65,4 +82,57 @@ describe('ReactDOMServerIntegration', () => {
82 expect(div.tagName).toBe('DIV');
83 expect(div.textContent).toBe('Test');
84 });
85 +
86 + describe('pure functional components', () => {
87 + beforeEach(() => {
88 + resetModules();
89 + });
90 +
91 + function Text({text}) {
92 + yieldValue(text);
93 + return <span>{text}</span>;
94 + }
95 +
96 + function Counter({count}) {
97 + return <Text text={'Count: ' + count} />;
98 + }
99 +
100 + itRenders('basic render', async render => {
101 + const PureCounter = pure(Counter);
102 + const domNode = await render(<PureCounter count={0} />);
103 + expect(domNode.textContent).toEqual('Count: 0');
104 + });
105 +
106 + itRenders('composition with forwardRef', async render => {
107 + const RefCounter = (props, ref) => <Counter count={ref.current} />;
108 + const PureRefCounter = pure(forwardRef(RefCounter));
109 +
110 + const ref = React.createRef();
111 + ref.current = 0;
112 + await render(<PureRefCounter ref={ref} />);
113 +
114 + expect(clearYields()).toEqual(['Count: 0']);
115 + });
116 +
117 + itRenders('with comparator', async render => {
118 + const PureCounter = pure(Counter, (oldProps, newProps) => false);
119 + await render(<PureCounter count={0} />);
120 + expect(clearYields()).toEqual(['Count: 0']);
121 + });
122 +
123 + itRenders(
124 + 'comparator functions are not invoked on the server',
125 + async render => {
126 + const PureCounter = React.pure(Counter, (oldProps, newProps) => {
127 + yieldValue(
128 + `Old count: ${oldProps.count}, New count: ${newProps.count}`,
129 + );
130 + return oldProps.count === newProps.count;
131 + });
132 +
133 + await render(<PureCounter count={0} />);
134 + expect(clearYields()).toEqual(['Count: 0']);
135 + },
136 + );
137 + });
138 });
packages/react-dom/src/server/ReactPartialRenderer.js
+23
@@ -39,6 +39,7 @@ import {
39 REACT_PROVIDER_TYPE,
40 REACT_CONTEXT_TYPE,
41 REACT_LAZY_TYPE,
42 + REACT_PURE_TYPE,
43 } from 'shared/ReactSymbols';
44
45 import {
@@ -1001,6 +1002,28 @@ class ReactDOMServerRenderer {
1002 this.stack.push(frame);
1003 return '';
1004 }
1005 + case REACT_PURE_TYPE: {
1006 + const element: ReactElement = ((nextChild: any): ReactElement);
1007 + let nextChildren = [
1008 + React.createElement(
1009 + elementType.type,
1010 + Object.assign({ref: element.ref}, element.props),
1011 + ),
1012 + ];
1013 + const frame: Frame = {
1014 + type: null,
1015 + domNamespace: parentNamespace,
1016 + children: nextChildren,
1017 + childIndex: 0,
1018 + context: context,
1019 + footer: '',
1020 + };
1021 + if (__DEV__) {
1022 + ((frame: any): FrameDev).debugElementStack = [];
1023 + }
1024 + this.stack.push(frame);
1025 + return '';
1026 + }
1027 case REACT_PROVIDER_TYPE: {
1028 const provider: ReactProvider<any> = (nextChild: any);
1029 const nextProps = provider.props;