Skip to content

Expanded DEV-only warnings for gDSFP and legacy lifecycles #12419

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

Merged
merged 7 commits into from
Mar 22, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 88 additions & 2 deletions packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ describe('ReactComponentLifeCycle', () => {

const container = document.createElement('div');
expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
'Defines both componentWillReceiveProps',
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.',
);
});

Expand All @@ -695,7 +695,93 @@ describe('ReactComponentLifeCycle', () => {

const container = document.createElement('div');
expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
'Defines both componentWillReceiveProps',
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.',
);
});

it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
const container = document.createElement('div');

class AllLegacyLifecycles extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
componentWillMount() {}
UNSAFE_componentWillReceiveProps() {}
componentWillUpdate() {}
render() {
return null;
}
}

expect(() => ReactDOM.render(<AllLegacyLifecycles />, container)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' componentWillMount\n' +
' UNSAFE_componentWillReceiveProps\n' +
' componentWillUpdate\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);

class WillMount extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
UNSAFE_componentWillMount() {}
render() {
return null;
}
}

expect(() => ReactDOM.render(<WillMount />, container)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' UNSAFE_componentWillMount\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);

class WillMountAndUpdate extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
componentWillMount() {}
UNSAFE_componentWillUpdate() {}
render() {
return null;
}
}

expect(() => ReactDOM.render(<WillMountAndUpdate />, container)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' componentWillMount\n' +
' UNSAFE_componentWillUpdate\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);

class WillReceiveProps extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
componentWillReceiveProps() {}
render() {
return null;
}
}

expect(() => ReactDOM.render(<WillReceiveProps />, container)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' componentWillReceiveProps\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);
});

Expand Down
109 changes: 71 additions & 38 deletions packages/react-reconciler/src/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ const isArray = Array.isArray;
let didWarnAboutStateAssignmentForComponent;
let didWarnAboutUndefinedDerivedState;
let didWarnAboutUninitializedState;
let didWarnAboutWillReceivePropsAndDerivedState;
let didWarnAboutLegacyLifecyclesAndDerivedState;
let warnOnInvalidCallback;

if (__DEV__) {
didWarnAboutStateAssignmentForComponent = {};
didWarnAboutUndefinedDerivedState = {};
didWarnAboutUninitializedState = {};
didWarnAboutWillReceivePropsAndDerivedState = {};
didWarnAboutLegacyLifecyclesAndDerivedState = {};

const didWarnOnInvalidCallback = {};

Expand Down Expand Up @@ -419,20 +419,75 @@ export default function(
adoptClassInstance(workInProgress, instance);

if (__DEV__) {
if (
typeof ctor.getDerivedStateFromProps === 'function' &&
state === null
) {
const componentName = getComponentName(workInProgress) || 'Component';
if (!didWarnAboutUninitializedState[componentName]) {
warning(
false,
'%s: Did not properly initialize state during construction. ' +
'Expected state to be an object, but it was %s.',
componentName,
instance.state === null ? 'null' : 'undefined',
);
didWarnAboutUninitializedState[componentName] = true;
if (typeof ctor.getDerivedStateFromProps === 'function') {
if (state === null) {
const componentName = getComponentName(workInProgress) || 'Component';
if (!didWarnAboutUninitializedState[componentName]) {
warning(
false,
'%s: Did not properly initialize state during construction. ' +
'Expected state to be an object, but it was %s.',
componentName,
instance.state === null ? 'null' : 'undefined',
);
didWarnAboutUninitializedState[componentName] = true;
}
}

// If getDerivedStateFromProps() is defined, "unsafe" lifecycles won't be called.
// Warn about these lifecycles if they are present.
// Don't warn about react-lifecycles-compat polyfilled methods though.
let foundWillMountName = null;
let foundWillReceivePropsName = null;
let foundWillUpdateName = null;
if (
typeof instance.componentWillMount === 'function' &&
instance.componentWillMount.__suppressDeprecationWarning !== true
) {
foundWillMountName = 'componentWillMount';
} else if (typeof instance.UNSAFE_componentWillMount === 'function') {
foundWillMountName = 'UNSAFE_componentWillMount';
}
if (
typeof instance.componentWillReceiveProps === 'function' &&
instance.componentWillReceiveProps.__suppressDeprecationWarning !==
true
) {
foundWillReceivePropsName = 'componentWillReceiveProps';
} else if (
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
) {
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
}
if (typeof instance.componentWillUpdate === 'function') {
foundWillUpdateName = 'componentWillUpdate';
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
}
if (
foundWillMountName !== null ||
foundWillReceivePropsName !== null ||
foundWillUpdateName !== null
) {
const componentName = getComponentName(workInProgress) || 'Component';
if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
warning(
false,
'Unsafe legacy lifecycles will not be called for components using ' +
'the new getDerivedStateFromProps() API.\n\n' +
'%s uses getDerivedStateFromProps() but also contains the following legacy lifecycles:' +
'%s%s%s\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
componentName,
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
foundWillReceivePropsName !== null
? `\n ${foundWillReceivePropsName}`
: '',
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
);
didWarnAboutLegacyLifecyclesAndDerivedState[componentName] = true;
}
}
}
}
Expand Down Expand Up @@ -536,28 +591,6 @@ export default function(
const {type} = workInProgress;

if (typeof type.getDerivedStateFromProps === 'function') {
if (__DEV__) {
// Don't warn about react-lifecycles-compat polyfilled components
if (
(typeof instance.componentWillReceiveProps === 'function' &&
instance.componentWillReceiveProps.__suppressDeprecationWarning !==
true) ||
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
) {
const componentName = getComponentName(workInProgress) || 'Component';
if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
warning(
false,
'%s: Defines both componentWillReceiveProps() and static ' +
'getDerivedStateFromProps() methods. We recommend using ' +
'only getDerivedStateFromProps().',
componentName,
);
didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
}
}
}

if (
debugRenderPhaseSideEffects ||
(debugRenderPhaseSideEffectsForStrictMode &&
Expand Down
64 changes: 51 additions & 13 deletions packages/react-test-renderer/src/ReactShallowRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ let didWarnAboutLegacyWillReceiveProps;
let didWarnAboutLegacyWillUpdate;
let didWarnAboutUndefinedDerivedState;
let didWarnAboutUninitializedState;
let didWarnAboutWillReceivePropsAndDerivedState;
let didWarnAboutLegacyLifecyclesAndDerivedState;

if (__DEV__) {
if (warnAboutDeprecatedLifecycles) {
Expand All @@ -33,7 +33,7 @@ if (__DEV__) {
}
didWarnAboutUndefinedDerivedState = {};
didWarnAboutUninitializedState = {};
didWarnAboutWillReceivePropsAndDerivedState = {};
didWarnAboutLegacyLifecyclesAndDerivedState = {};
}

class ReactShallowRenderer {
Expand Down Expand Up @@ -352,23 +352,61 @@ class ReactShallowRenderer {

if (typeof type.getDerivedStateFromProps === 'function') {
if (__DEV__) {
// Don't warn about react-lifecycles-compat polyfilled components
const instance = this._instance;

// If getDerivedStateFromProps() is defined, "unsafe" lifecycles won't be called.
// Warn about these lifecycles if they are present.
// Don't warn about react-lifecycles-compat polyfilled methods though.
let foundWillMountName = null;
let foundWillReceivePropsName = null;
let foundWillUpdateName = null;
if (
(typeof this._instance.componentWillReceiveProps === 'function' &&
this._instance.componentWillReceiveProps
.__suppressDeprecationWarning !== true) ||
typeof this._instance.UNSAFE_componentWillReceiveProps === 'function'
typeof instance.componentWillMount === 'function' &&
instance.componentWillMount.__suppressDeprecationWarning !== true
) {
const componentName = getName(type, this._instance);
if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
foundWillMountName = 'componentWillMount';
} else if (typeof instance.UNSAFE_componentWillMount === 'function') {
foundWillMountName = 'UNSAFE_componentWillMount';
}
if (
typeof instance.componentWillReceiveProps === 'function' &&
instance.componentWillReceiveProps.__suppressDeprecationWarning !==
true
) {
foundWillReceivePropsName = 'componentWillReceiveProps';
} else if (
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
) {
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
}
if (typeof instance.componentWillUpdate === 'function') {
foundWillUpdateName = 'componentWillUpdate';
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
}
if (
foundWillMountName !== null ||
foundWillReceivePropsName !== null ||
foundWillUpdateName !== null
) {
const componentName = getName(type, instance) || 'Component';
if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
warning(
false,
'%s: Defines both componentWillReceiveProps() and static ' +
'getDerivedStateFromProps() methods. We recommend using ' +
'only getDerivedStateFromProps().',
'Unsafe legacy lifecycles will not be called for components using ' +
'the new getDerivedStateFromProps() API.\n\n' +
'%s uses getDerivedStateFromProps() but also contains the following legacy lifecycles:' +
'%s%s%s\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
componentName,
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
foundWillReceivePropsName !== null
? `\n ${foundWillReceivePropsName}`
: '',
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
);
didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
didWarnAboutLegacyLifecyclesAndDerivedState[componentName] = true;
}
}
}
Expand Down
Loading