Skip to content

Commit

Permalink
Added static getDerivedStateFromProps to ReactPartialRenderer
Browse files Browse the repository at this point in the history
Also added a new set of tests focused on server side lifecycle hooks.
  • Loading branch information
bvaughn committed Jan 17, 2018
1 parent 8679926 commit 64f27d7
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 0 deletions.
150 changes: 150 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js
@@ -0,0 +1,150 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/

'use strict';

const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOMServer;

function initModules() {
// Reset warning cache.
jest.resetModuleRegistry();
React = require('react');
ReactDOMServer = require('react-dom/server');

// Make them available to the helpers.
return {
ReactDOMServer,
};
}

const {resetModules} = ReactDOMServerIntegrationUtils(initModules);

describe('ReactDOMServerLifecycles', () => {
beforeEach(() => {
resetModules();
});

it('should invoke the correct lifecycle hooks', () => {
const log = [];

class Outer extends React.Component {
unsafe_componentWillMount() {
log.push('outer componentWillMount');
}
render() {
log.push('outer render');
return <Inner />;
}
}

class Inner extends React.Component {
unsafe_componentWillMount() {
log.push('inner componentWillMount');
}
render() {
log.push('inner render');
return null;
}
}

ReactDOMServer.renderToString(<Outer />);
expect(log).toEqual([
'outer componentWillMount',
'outer render',
'inner componentWillMount',
'inner render',
]);
});

it('should warn about deprecated lifecycle hooks', () => {
class Component extends React.Component {
componentWillMount() {}
render() {
return null;
}
}

expect(() => ReactDOMServer.renderToString(<Component />)).toWarnDev(
'Warning: Component: componentWillMount() is deprecated and will be removed ' +
'in the next major version. Please use unsafe_componentWillMount() instead.',
);
});

it('should update instance.state with value returned from getDerivedStateFromProps', () => {
class Grandparent extends React.Component {
state = {
foo: 'foo',
};
render() {
return (
<div>
{`Grandparent: ${this.state.foo}`}
<Parent />
</div>
);
}
}

class Parent extends React.Component {
state = {
bar: 'bar',
baz: 'baz',
};
static getDerivedStateFromProps(props, prevState) {
return {
bar: `not ${prevState.bar}`,
};
}
render() {
return (
<div>
{`Parent: ${this.state.bar}, ${this.state.baz}`}
<Child />;
</div>
);
}
}

class Child extends React.Component {
static getDerivedStateFromProps() {
return {
qux: 'qux',
};
}
render() {
return `Child: ${this.state.qux}`;
}
}

const markup = ReactDOMServer.renderToString(<Grandparent />);
expect(markup).toContain('Grandparent: foo');
expect(markup).toContain('Parent: not bar, baz');
expect(markup).toContain('Child: qux');
});

it('should warn if getDerivedStateFromProps returns undefined', () => {
class Component extends React.Component {
static getDerivedStateFromProps() {}
render() {
return null;
}
}

expect(() => ReactDOMServer.renderToString(<Component />)).toWarnDev(
'Component.getDerivedStateFromProps(): A valid state object (or null) must ' +
'be returned. You may have returned undefined.',
);

// De-duped
ReactDOMServer.renderToString(<Component />);
});
});
28 changes: 28 additions & 0 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Expand Up @@ -123,6 +123,7 @@ let didWarnDefaultTextareaValue = false;
let didWarnInvalidOptionChildren = false;
const didWarnAboutNoopUpdateForComponent = {};
const didWarnAboutBadClass = {};
const didWarnAboutUndefinedDerivedState = {};
const valuePropNames = ['value', 'defaultValue'];
const newlineEatingTags = {
listing: true,
Expand Down Expand Up @@ -421,6 +422,33 @@ function resolve(

if (shouldConstruct(Component)) {
inst = new Component(element.props, publicContext, updater);

if (typeof Component.getDerivedStateFromProps === 'function') {
partialState = Component.getDerivedStateFromProps(
element.props,
inst.state,
);

if (__DEV__) {
if (partialState === undefined) {
const componentName = getComponentName(Component) || 'Unknown';

if (!didWarnAboutUndefinedDerivedState[componentName]) {
warning(
false,
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
'You may have returned undefined.',
componentName,
);
didWarnAboutUndefinedDerivedState[componentName] = true;
}
}
}

if (partialState != null) {
inst.state = Object.assign({}, inst.state, partialState);
}
}
} else {
if (__DEV__) {
if (
Expand Down

0 comments on commit 64f27d7

Please sign in to comment.