Skip to content

Commit 8e89c06

Browse files
alexmckenleylinjiajian999
authored andcommitted
Add support for React.pure in ReactDOMServer (facebook#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.
1 parent 5783a9b commit 8e89c06

2 files changed

Lines changed: 94 additions & 1 deletion

File tree

packages/react-dom/src/__tests__/ReactDOMServerIntegrationSpecialTypes-test.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,30 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
1414
let React;
1515
let ReactDOM;
1616
let ReactDOMServer;
17+
let forwardRef;
18+
let pure;
19+
let yieldedValues;
20+
let yieldValue;
21+
let clearYields;
1722

1823
function initModules() {
1924
// Reset warning cache.
2025
jest.resetModuleRegistry();
2126
React = require('react');
2227
ReactDOM = require('react-dom');
2328
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+
};
2441

2542
// Make them available to the helpers.
2643
return {
@@ -40,7 +57,7 @@ describe('ReactDOMServerIntegration', () => {
4057
const FunctionComponent = ({label, forwardedRef}) => (
4158
<div ref={forwardedRef}>{label}</div>
4259
);
43-
const WrappedFunctionComponent = React.forwardRef((props, ref) => (
60+
const WrappedFunctionComponent = forwardRef((props, ref) => (
4461
<FunctionComponent {...props} forwardedRef={ref} />
4562
));
4663

@@ -65,4 +82,57 @@ describe('ReactDOMServerIntegration', () => {
6582
expect(div.tagName).toBe('DIV');
6683
expect(div.textContent).toBe('Test');
6784
});
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+
});
68138
});

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
REACT_PROVIDER_TYPE,
4040
REACT_CONTEXT_TYPE,
4141
REACT_LAZY_TYPE,
42+
REACT_PURE_TYPE,
4243
} from 'shared/ReactSymbols';
4344

4445
import {
@@ -1001,6 +1002,28 @@ class ReactDOMServerRenderer {
10011002
this.stack.push(frame);
10021003
return '';
10031004
}
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+
}
10041027
case REACT_PROVIDER_TYPE: {
10051028
const provider: ReactProvider<any> = (nextChild: any);
10061029
const nextProps = provider.props;

0 commit comments

Comments
 (0)