Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for React.pure in ReactDOMServer #13855

Merged
merged 2 commits into from
Oct 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,30 @@ 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.
jest.resetModuleRegistry();
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 {
Expand All @@ -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} />
));

Expand All @@ -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 changes: 23 additions & 0 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
REACT_PROVIDER_TYPE,
REACT_CONTEXT_TYPE,
REACT_LAZY_TYPE,
REACT_PURE_TYPE,
} from 'shared/ReactSymbols';

import {
Expand Down Expand Up @@ -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;
Expand Down