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;