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.
This commit is contained in:
Alex Taylor authored and Sebastian Markbåge committed 2018-10-20 00:42:33 -07:00
1 parent 15b11d23f9
commit 8ecd4bd4f0
2 files changed
+94 -1

No files matched your search

@@ -14,6 +14,11 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
let React;
let ReactDOM;
let ReactDOMServer;
let forwardRef;
let pure;
let yieldedValues;
let yieldValue;
let clearYields;
function initModules() {
// Reset warning cache.
@@ -21,6 +26,18 @@ function initModules() {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
forwardRef = React.forwardRef;
pure = React.pure;
yieldedValues = [];
yieldValue = value => {
yieldedValues.push(value);
};
clearYields = () => {
const ret = yieldedValues;
yieldedValues = [];
return ret;
};
// Make them available to the helpers.
return {
@@ -40,7 +57,7 @@ describe('ReactDOMServerIntegration', () => {
const FunctionComponent = ({label, forwardedRef}) => (
<div ref={forwardedRef}>{label}</div>
);
const WrappedFunctionComponent = React.forwardRef((props, ref) => (
const WrappedFunctionComponent = forwardRef((props, ref) => (
<FunctionComponent {...props} forwardedRef={ref} />
));
@@ -65,4 +82,57 @@ describe('ReactDOMServerIntegration', () => {
expect(div.tagName).toBe('DIV');
expect(div.textContent).toBe('Test');
});
describe('pure functional components', () => {
beforeEach(() => {
resetModules();
});
function Text({text}) {
yieldValue(text);
return <span>{text}</span>;
}
function Counter({count}) {
return <Text text={'Count: ' + count} />;
}
itRenders('basic render', async render => {
const PureCounter = pure(Counter);
const domNode = await render(<PureCounter count={0} />);
expect(domNode.textContent).toEqual('Count: 0');
});
itRenders('composition with forwardRef', async render => {
const RefCounter = (props, ref) => <Counter count={ref.current} />;
const PureRefCounter = pure(forwardRef(RefCounter));
const ref = React.createRef();
ref.current = 0;
await render(<PureRefCounter ref={ref} />);
expect(clearYields()).toEqual(['Count: 0']);
});
itRenders('with comparator', async render => {
const PureCounter = pure(Counter, (oldProps, newProps) => false);
await render(<PureCounter count={0} />);
expect(clearYields()).toEqual(['Count: 0']);
});
itRenders(
'comparator functions are not invoked on the server',
async render => {
const PureCounter = React.pure(Counter, (oldProps, newProps) => {
yieldValue(
`Old count: ${oldProps.count}, New count: ${newProps.count}`,
);
return oldProps.count === newProps.count;
});
await render(<PureCounter count={0} />);
expect(clearYields()).toEqual(['Count: 0']);
},
);
});
});
+23
View File
@@ -39,6 +39,7 @@ import {
REACT_PROVIDER_TYPE,
REACT_CONTEXT_TYPE,
REACT_LAZY_TYPE,
REACT_PURE_TYPE,
} from 'shared/ReactSymbols';
import {
@@ -1001,6 +1002,28 @@ class ReactDOMServerRenderer {
this.stack.push(frame);
return '';
}
case REACT_PURE_TYPE: {
const element: ReactElement = ((nextChild: any): ReactElement);
let nextChildren = [
React.createElement(
elementType.type,
Object.assign({ref: element.ref}, element.props),
),
];
const frame: Frame = {
type: null,
domNamespace: parentNamespace,
children: nextChildren,
childIndex: 0,
context: context,
footer: '',
};
if (__DEV__) {
((frame: any): FrameDev).debugElementStack = [];
}
this.stack.push(frame);
return '';
}
case REACT_PROVIDER_TYPE: {
const provider: ReactProvider<any> = (nextChild: any);
const nextProps = provider.props;