Skip to content

Commit

Permalink
Added getDerivedStateFromProps to shallow renderer
Browse files Browse the repository at this point in the history
Also added warnings for several cases involving getDerivedStateFromProps() as well as the deprecated lifecycles.
Also added tests for the above.
  • Loading branch information
bvaughn committed Jan 17, 2018
1 parent 64f27d7 commit 1047182
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 24 deletions.
118 changes: 100 additions & 18 deletions packages/react-test-renderer/src/ReactShallowRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ class ReactShallowRenderer {
this._rendering = false;
this._forcedUpdate = false;
this._updater = new Updater(this);

if (__DEV__) {
this._didWarnAboutLegacyWillMount = {};
this._didWarnAboutLegacyWillReceiveProps = {};
this._didWarnAboutLegacyWillUpdate = {};
this._didWarnAboutUndefinedDerivedState = {};
this._didWarnAboutWillReceivePropsAndDerivedState = {};
}
}

getMountedInstance() {
Expand Down Expand Up @@ -83,6 +91,8 @@ class ReactShallowRenderer {
this._updater,
);

this._updateStateFromStaticLifecycle(element.props);

if (element.type.hasOwnProperty('contextTypes')) {
currentlyValidatingElement = element;

Expand Down Expand Up @@ -137,12 +147,16 @@ class ReactShallowRenderer {

if (typeof this._instance.componentWillMount === 'function') {
if (__DEV__) {
warning(
false,
'%s: componentWillMount() is deprecated and will be removed in the ' +
'next major version. Please use unsafe_componentWillMount() instead.',
getName(element.type, this._instance),
);
const componentName = getName(element.type, this._instance);
if (!this._didWarnAboutLegacyWillMount[componentName]) {
warning(
false,
'%s: componentWillMount() is deprecated and will be removed in the ' +
'next major version. Please use unsafe_componentWillMount() instead.',
componentName,
);
this._didWarnAboutLegacyWillMount[componentName] = true;
}
}
this._instance.componentWillMount();
} else {
Expand All @@ -169,20 +183,27 @@ class ReactShallowRenderer {
if (oldProps !== props) {
if (typeof this._instance.componentWillReceiveProps === 'function') {
if (__DEV__) {
warning(
false,
'%s: componentWillReceiveProps() is deprecated and will be removed in the ' +
'next major version. Please use unsafe_componentWillReceiveProps() instead.',
getName(element.type, this._instance),
);
const componentName = getName(element.type, this._instance);
if (!this._didWarnAboutLegacyWillReceiveProps[componentName]) {
warning(
false,
'%s: componentWillReceiveProps() is deprecated and will be removed in the ' +
'next major version. Please use unsafe_componentWillReceiveProps() instead.',
componentName,
);
this._didWarnAboutLegacyWillReceiveProps[componentName] = true;
}
}
this._instance.componentWillReceiveProps(props, context);
} else if (
typeof this._instance.unsafe_componentWillReceiveProps === 'function'
) {
this._instance.unsafe_componentWillReceiveProps(props, context);
}

this._updateStateFromStaticLifecycle(props);
}

// Read state after cWRP in case it calls setState
const state = this._newState || oldState;

Expand All @@ -204,12 +225,16 @@ class ReactShallowRenderer {
if (shouldUpdate) {
if (typeof this._instance.componentWillUpdate === 'function') {
if (__DEV__) {
warning(
false,
'%s: componentWillUpdate() is deprecated and will be removed in the ' +
'next major version. Please use unsafe_componentWillUpdate() instead.',
getName(element.type, this._instance),
);
const componentName = getName(element.type, this._instance);
if (!this._didWarnAboutLegacyWillUpdate[componentName]) {
warning(
false,
'%s: componentWillUpdate() is deprecated and will be removed in the ' +
'next major version. Please use unsafe_componentWillUpdate() instead.',
componentName,
);
this._didWarnAboutLegacyWillUpdate[componentName] = true;
}
}

this._instance.componentWillUpdate(props, state, context);
Expand All @@ -230,6 +255,63 @@ class ReactShallowRenderer {
// Intentionally do not call componentDidUpdate()
// because DOM refs are not available.
}

_updateStateFromStaticLifecycle(props) {
const {type} = this._element;

if (typeof type.getDerivedStateFromProps === 'function') {
if (__DEV__) {
if (
typeof this._instance.componentWillReceiveProps === 'function' ||
typeof this._instance.unsafe_componentWillReceiveProps === 'function'
) {
const componentName = getName(type, this._instance);
if (
!this._didWarnAboutWillReceivePropsAndDerivedState[componentName]
) {
warning(
false,
'%s: Defines both componentWillReceiveProps() and static ' +
'getDerivedStateFromProps() methods. We recommend using ' +
'only getDerivedStateFromProps().',
componentName,
);
this._didWarnAboutWillReceivePropsAndDerivedState[
componentName
] = true;
}
}
}

const partialState = type.getDerivedStateFromProps(
props,
this._instance.state,
);

if (__DEV__) {
if (partialState === undefined) {
const componentName = getName(type, this._instance);
if (!this._didWarnAboutUndefinedDerivedState[componentName]) {
warning(
false,
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
'You may have returned undefined.',
componentName,
);
this._didWarnAboutUndefinedDerivedState[
componentName
] = componentName;
}
}
}

if (partialState) {
const oldState = this._newState || this._instance.state;
const newState = Object.assign({}, oldState, partialState);
this._instance.state = this._newState = newState;
}
}
}
}

class Updater {
Expand Down
Loading

0 comments on commit 1047182

Please sign in to comment.