Skip to content

Commit 07ce77e

Browse files
bvaughnLeonYuAng3NT
authored andcommitted
Expanded DEV-only warnings for gDSFP and legacy lifecycles (facebook#12419)
1 parent fe94472 commit 07ce77e

File tree

5 files changed

+312
-59
lines changed

5 files changed

+312
-59
lines changed

packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ describe('ReactComponentLifeCycle', () => {
669669

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

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

696696
const container = document.createElement('div');
697697
expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
698-
'Defines both componentWillReceiveProps',
698+
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.',
699+
);
700+
});
701+
702+
it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
703+
const container = document.createElement('div');
704+
705+
class AllLegacyLifecycles extends React.Component {
706+
state = {};
707+
static getDerivedStateFromProps() {
708+
return null;
709+
}
710+
componentWillMount() {}
711+
UNSAFE_componentWillReceiveProps() {}
712+
componentWillUpdate() {}
713+
render() {
714+
return null;
715+
}
716+
}
717+
718+
expect(() => ReactDOM.render(<AllLegacyLifecycles />, container)).toWarnDev(
719+
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
720+
'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
721+
' componentWillMount\n' +
722+
' UNSAFE_componentWillReceiveProps\n' +
723+
' componentWillUpdate\n\n' +
724+
'The above lifecycles should be removed. Learn more about this warning here:\n' +
725+
'https://fb.me/react-async-component-lifecycle-hooks',
726+
);
727+
728+
class WillMount extends React.Component {
729+
state = {};
730+
static getDerivedStateFromProps() {
731+
return null;
732+
}
733+
UNSAFE_componentWillMount() {}
734+
render() {
735+
return null;
736+
}
737+
}
738+
739+
expect(() => ReactDOM.render(<WillMount />, container)).toWarnDev(
740+
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
741+
'WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
742+
' UNSAFE_componentWillMount\n\n' +
743+
'The above lifecycles should be removed. Learn more about this warning here:\n' +
744+
'https://fb.me/react-async-component-lifecycle-hooks',
745+
);
746+
747+
class WillMountAndUpdate extends React.Component {
748+
state = {};
749+
static getDerivedStateFromProps() {
750+
return null;
751+
}
752+
componentWillMount() {}
753+
UNSAFE_componentWillUpdate() {}
754+
render() {
755+
return null;
756+
}
757+
}
758+
759+
expect(() => ReactDOM.render(<WillMountAndUpdate />, container)).toWarnDev(
760+
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
761+
'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
762+
' componentWillMount\n' +
763+
' UNSAFE_componentWillUpdate\n\n' +
764+
'The above lifecycles should be removed. Learn more about this warning here:\n' +
765+
'https://fb.me/react-async-component-lifecycle-hooks',
766+
);
767+
768+
class WillReceiveProps extends React.Component {
769+
state = {};
770+
static getDerivedStateFromProps() {
771+
return null;
772+
}
773+
componentWillReceiveProps() {}
774+
render() {
775+
return null;
776+
}
777+
}
778+
779+
expect(() => ReactDOM.render(<WillReceiveProps />, container)).toWarnDev(
780+
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
781+
'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
782+
' componentWillReceiveProps\n\n' +
783+
'The above lifecycles should be removed. Learn more about this warning here:\n' +
784+
'https://fb.me/react-async-component-lifecycle-hooks',
699785
);
700786
});
701787

packages/react-reconciler/src/ReactFiberClassComponent.js

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ const isArray = Array.isArray;
4141
let didWarnAboutStateAssignmentForComponent;
4242
let didWarnAboutUndefinedDerivedState;
4343
let didWarnAboutUninitializedState;
44-
let didWarnAboutWillReceivePropsAndDerivedState;
44+
let didWarnAboutLegacyLifecyclesAndDerivedState;
4545
let warnOnInvalidCallback;
4646

4747
if (__DEV__) {
4848
didWarnAboutStateAssignmentForComponent = {};
4949
didWarnAboutUndefinedDerivedState = {};
5050
didWarnAboutUninitializedState = {};
51-
didWarnAboutWillReceivePropsAndDerivedState = {};
51+
didWarnAboutLegacyLifecyclesAndDerivedState = {};
5252

5353
const didWarnOnInvalidCallback = {};
5454

@@ -435,20 +435,75 @@ export default function(
435435
adoptClassInstance(workInProgress, instance);
436436

437437
if (__DEV__) {
438-
if (
439-
typeof ctor.getDerivedStateFromProps === 'function' &&
440-
state === null
441-
) {
442-
const componentName = getComponentName(workInProgress) || 'Component';
443-
if (!didWarnAboutUninitializedState[componentName]) {
444-
warning(
445-
false,
446-
'%s: Did not properly initialize state during construction. ' +
447-
'Expected state to be an object, but it was %s.',
448-
componentName,
449-
instance.state === null ? 'null' : 'undefined',
450-
);
451-
didWarnAboutUninitializedState[componentName] = true;
438+
if (typeof ctor.getDerivedStateFromProps === 'function') {
439+
if (state === null) {
440+
const componentName = getComponentName(workInProgress) || 'Component';
441+
if (!didWarnAboutUninitializedState[componentName]) {
442+
warning(
443+
false,
444+
'%s: Did not properly initialize state during construction. ' +
445+
'Expected state to be an object, but it was %s.',
446+
componentName,
447+
instance.state === null ? 'null' : 'undefined',
448+
);
449+
didWarnAboutUninitializedState[componentName] = true;
450+
}
451+
}
452+
453+
// If getDerivedStateFromProps() is defined, "unsafe" lifecycles won't be called.
454+
// Warn about these lifecycles if they are present.
455+
// Don't warn about react-lifecycles-compat polyfilled methods though.
456+
let foundWillMountName = null;
457+
let foundWillReceivePropsName = null;
458+
let foundWillUpdateName = null;
459+
if (
460+
typeof instance.componentWillMount === 'function' &&
461+
instance.componentWillMount.__suppressDeprecationWarning !== true
462+
) {
463+
foundWillMountName = 'componentWillMount';
464+
} else if (typeof instance.UNSAFE_componentWillMount === 'function') {
465+
foundWillMountName = 'UNSAFE_componentWillMount';
466+
}
467+
if (
468+
typeof instance.componentWillReceiveProps === 'function' &&
469+
instance.componentWillReceiveProps.__suppressDeprecationWarning !==
470+
true
471+
) {
472+
foundWillReceivePropsName = 'componentWillReceiveProps';
473+
} else if (
474+
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
475+
) {
476+
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
477+
}
478+
if (typeof instance.componentWillUpdate === 'function') {
479+
foundWillUpdateName = 'componentWillUpdate';
480+
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
481+
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
482+
}
483+
if (
484+
foundWillMountName !== null ||
485+
foundWillReceivePropsName !== null ||
486+
foundWillUpdateName !== null
487+
) {
488+
const componentName = getComponentName(workInProgress) || 'Component';
489+
if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
490+
warning(
491+
false,
492+
'Unsafe legacy lifecycles will not be called for components using ' +
493+
'the new getDerivedStateFromProps() API.\n\n' +
494+
'%s uses getDerivedStateFromProps() but also contains the following legacy lifecycles:' +
495+
'%s%s%s\n\n' +
496+
'The above lifecycles should be removed. Learn more about this warning here:\n' +
497+
'https://fb.me/react-async-component-lifecycle-hooks',
498+
componentName,
499+
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
500+
foundWillReceivePropsName !== null
501+
? `\n ${foundWillReceivePropsName}`
502+
: '',
503+
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
504+
);
505+
didWarnAboutLegacyLifecyclesAndDerivedState[componentName] = true;
506+
}
452507
}
453508
}
454509
}
@@ -552,28 +607,6 @@ export default function(
552607
const {type} = workInProgress;
553608

554609
if (typeof type.getDerivedStateFromProps === 'function') {
555-
if (__DEV__) {
556-
// Don't warn about react-lifecycles-compat polyfilled components
557-
if (
558-
(typeof instance.componentWillReceiveProps === 'function' &&
559-
instance.componentWillReceiveProps.__suppressDeprecationWarning !==
560-
true) ||
561-
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
562-
) {
563-
const componentName = getComponentName(workInProgress) || 'Component';
564-
if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
565-
warning(
566-
false,
567-
'%s: Defines both componentWillReceiveProps() and static ' +
568-
'getDerivedStateFromProps() methods. We recommend using ' +
569-
'only getDerivedStateFromProps().',
570-
componentName,
571-
);
572-
didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
573-
}
574-
}
575-
}
576-
577610
if (
578611
debugRenderPhaseSideEffects ||
579612
(debugRenderPhaseSideEffectsForStrictMode &&

packages/react-test-renderer/src/ReactShallowRenderer.js

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ let didWarnAboutLegacyWillReceiveProps;
2323
let didWarnAboutLegacyWillUpdate;
2424
let didWarnAboutUndefinedDerivedState;
2525
let didWarnAboutUninitializedState;
26-
let didWarnAboutWillReceivePropsAndDerivedState;
26+
let didWarnAboutLegacyLifecyclesAndDerivedState;
2727

2828
if (__DEV__) {
2929
if (warnAboutDeprecatedLifecycles) {
@@ -33,7 +33,7 @@ if (__DEV__) {
3333
}
3434
didWarnAboutUndefinedDerivedState = {};
3535
didWarnAboutUninitializedState = {};
36-
didWarnAboutWillReceivePropsAndDerivedState = {};
36+
didWarnAboutLegacyLifecyclesAndDerivedState = {};
3737
}
3838

3939
class ReactShallowRenderer {
@@ -352,23 +352,61 @@ class ReactShallowRenderer {
352352

353353
if (typeof type.getDerivedStateFromProps === 'function') {
354354
if (__DEV__) {
355-
// Don't warn about react-lifecycles-compat polyfilled components
355+
const instance = this._instance;
356+
357+
// If getDerivedStateFromProps() is defined, "unsafe" lifecycles won't be called.
358+
// Warn about these lifecycles if they are present.
359+
// Don't warn about react-lifecycles-compat polyfilled methods though.
360+
let foundWillMountName = null;
361+
let foundWillReceivePropsName = null;
362+
let foundWillUpdateName = null;
356363
if (
357-
(typeof this._instance.componentWillReceiveProps === 'function' &&
358-
this._instance.componentWillReceiveProps
359-
.__suppressDeprecationWarning !== true) ||
360-
typeof this._instance.UNSAFE_componentWillReceiveProps === 'function'
364+
typeof instance.componentWillMount === 'function' &&
365+
instance.componentWillMount.__suppressDeprecationWarning !== true
361366
) {
362-
const componentName = getName(type, this._instance);
363-
if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
367+
foundWillMountName = 'componentWillMount';
368+
} else if (typeof instance.UNSAFE_componentWillMount === 'function') {
369+
foundWillMountName = 'UNSAFE_componentWillMount';
370+
}
371+
if (
372+
typeof instance.componentWillReceiveProps === 'function' &&
373+
instance.componentWillReceiveProps.__suppressDeprecationWarning !==
374+
true
375+
) {
376+
foundWillReceivePropsName = 'componentWillReceiveProps';
377+
} else if (
378+
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
379+
) {
380+
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
381+
}
382+
if (typeof instance.componentWillUpdate === 'function') {
383+
foundWillUpdateName = 'componentWillUpdate';
384+
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
385+
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
386+
}
387+
if (
388+
foundWillMountName !== null ||
389+
foundWillReceivePropsName !== null ||
390+
foundWillUpdateName !== null
391+
) {
392+
const componentName = getName(type, instance) || 'Component';
393+
if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
364394
warning(
365395
false,
366-
'%s: Defines both componentWillReceiveProps() and static ' +
367-
'getDerivedStateFromProps() methods. We recommend using ' +
368-
'only getDerivedStateFromProps().',
396+
'Unsafe legacy lifecycles will not be called for components using ' +
397+
'the new getDerivedStateFromProps() API.\n\n' +
398+
'%s uses getDerivedStateFromProps() but also contains the following legacy lifecycles:' +
399+
'%s%s%s\n\n' +
400+
'The above lifecycles should be removed. Learn more about this warning here:\n' +
401+
'https://fb.me/react-async-component-lifecycle-hooks',
369402
componentName,
403+
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
404+
foundWillReceivePropsName !== null
405+
? `\n ${foundWillReceivePropsName}`
406+
: '',
407+
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
370408
);
371-
didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
409+
didWarnAboutLegacyLifecyclesAndDerivedState[componentName] = true;
372410
}
373411
}
374412
}

0 commit comments

Comments
 (0)