Expanded DEV-only warnings for gDSFP and legacy lifecycles (#12419)
Brian Vaughn committed
Mar 22, 2018 at 11:16 UTC
c1308adb4bbe9e5513bbc4499e0696d97e9c897f
5 files changed
+312
-59
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+88
-2
@@ -669,7 +669,7 @@ describe('ReactComponentLifeCycle', () => {
669
670
const container = document.createElement('div');
671
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.',
673
);
674
});
675
@@ -695,7 +695,93 @@ describe('ReactComponentLifeCycle', () => {
695
696
const container = document.createElement('div');
697
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',
785
);
786
});
787
packages/react-reconciler/src/ReactFiberClassComponent.js
+71
-38
@@ -41,14 +41,14 @@ const isArray = Array.isArray;
41
let didWarnAboutStateAssignmentForComponent;
42
let didWarnAboutUndefinedDerivedState;
43
let didWarnAboutUninitializedState;
44
-let didWarnAboutWillReceivePropsAndDerivedState;
44
+let didWarnAboutLegacyLifecyclesAndDerivedState;
45
let warnOnInvalidCallback;
46
47
if (__DEV__) {
48
didWarnAboutStateAssignmentForComponent = {};
49
didWarnAboutUndefinedDerivedState = {};
50
didWarnAboutUninitializedState = {};
51
- didWarnAboutWillReceivePropsAndDerivedState = {};
51
+ didWarnAboutLegacyLifecyclesAndDerivedState = {};
52
53
const didWarnOnInvalidCallback = {};
54
@@ -435,20 +435,75 @@ export default function(
435
adoptClassInstance(workInProgress, instance);
436
437
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
+ }
507
}
508
}
509
}
@@ -552,28 +607,6 @@ export default function(
607
const {type} = workInProgress;
608
609
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
-
610
if (
611
debugRenderPhaseSideEffects ||
612
(debugRenderPhaseSideEffectsForStrictMode &&
packages/react-test-renderer/src/ReactShallowRenderer.js
+51
-13
@@ -23,7 +23,7 @@ let didWarnAboutLegacyWillReceiveProps;
23
let didWarnAboutLegacyWillUpdate;
24
let didWarnAboutUndefinedDerivedState;
25
let didWarnAboutUninitializedState;
26
-let didWarnAboutWillReceivePropsAndDerivedState;
26
+let didWarnAboutLegacyLifecyclesAndDerivedState;
27
28
if (__DEV__) {
29
if (warnAboutDeprecatedLifecycles) {
@@ -33,7 +33,7 @@ if (__DEV__) {
33
}
34
didWarnAboutUndefinedDerivedState = {};
35
didWarnAboutUninitializedState = {};
36
- didWarnAboutWillReceivePropsAndDerivedState = {};
36
+ didWarnAboutLegacyLifecyclesAndDerivedState = {};
37
}
38
39
class ReactShallowRenderer {
@@ -352,23 +352,61 @@ class ReactShallowRenderer {
352
353
if (typeof type.getDerivedStateFromProps === 'function') {
354
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;
363
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
366
) {
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]) {
394
warning(
395
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',
402
componentName,
403
+ foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
404
+ foundWillReceivePropsName !== null
405
+ ? `\n ${foundWillReceivePropsName}`
406
+ : '',
407
+ foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
408
);
371
- didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
409
+ didWarnAboutLegacyLifecyclesAndDerivedState[componentName] = true;
410
}
411
}
412
}
packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js
+93
-5
@@ -125,8 +125,98 @@ describe('ReactShallowRenderer', () => {
125
}
126
127
const shallowRenderer = createRenderer();
128
- expect(() => shallowRenderer.render(<Component foo={2} />)).toWarnDev(
129
- 'Defines both componentWillReceiveProps() and static getDerivedStateFromProps()',
128
+ expect(() => shallowRenderer.render(<Component />)).toWarnDev(
129
+ 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.',
130
+ );
131
+ });
132
+
133
+ it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
134
+ let shallowRenderer;
135
+
136
+ class AllLegacyLifecycles extends React.Component {
137
+ state = {};
138
+ static getDerivedStateFromProps() {
139
+ return null;
140
+ }
141
+ componentWillMount() {}
142
+ UNSAFE_componentWillReceiveProps() {}
143
+ componentWillUpdate() {}
144
+ render() {
145
+ return null;
146
+ }
147
+ }
148
+
149
+ shallowRenderer = createRenderer();
150
+ expect(() => shallowRenderer.render(<AllLegacyLifecycles />)).toWarnDev(
151
+ 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
152
+ 'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
153
+ ' componentWillMount\n' +
154
+ ' UNSAFE_componentWillReceiveProps\n' +
155
+ ' componentWillUpdate\n\n' +
156
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
157
+ 'https://fb.me/react-async-component-lifecycle-hooks',
158
+ );
159
+
160
+ class WillMount extends React.Component {
161
+ state = {};
162
+ static getDerivedStateFromProps() {
163
+ return null;
164
+ }
165
+ UNSAFE_componentWillMount() {}
166
+ render() {
167
+ return null;
168
+ }
169
+ }
170
+
171
+ shallowRenderer = createRenderer();
172
+ expect(() => shallowRenderer.render(<WillMount />)).toWarnDev(
173
+ 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
174
+ 'WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
175
+ ' UNSAFE_componentWillMount\n\n' +
176
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
177
+ 'https://fb.me/react-async-component-lifecycle-hooks',
178
+ );
179
+
180
+ class WillMountAndUpdate extends React.Component {
181
+ state = {};
182
+ static getDerivedStateFromProps() {
183
+ return null;
184
+ }
185
+ componentWillMount() {}
186
+ UNSAFE_componentWillUpdate() {}
187
+ render() {
188
+ return null;
189
+ }
190
+ }
191
+
192
+ shallowRenderer = createRenderer();
193
+ expect(() => shallowRenderer.render(<WillMountAndUpdate />)).toWarnDev(
194
+ 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
195
+ 'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
196
+ ' componentWillMount\n' +
197
+ ' UNSAFE_componentWillUpdate\n\n' +
198
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
199
+ 'https://fb.me/react-async-component-lifecycle-hooks',
200
+ );
201
+
202
+ class WillReceiveProps extends React.Component {
203
+ state = {};
204
+ static getDerivedStateFromProps() {
205
+ return null;
206
+ }
207
+ componentWillReceiveProps() {}
208
+ render() {
209
+ return null;
210
+ }
211
+ }
212
+
213
+ shallowRenderer = createRenderer();
214
+ expect(() => shallowRenderer.render(<WillReceiveProps />)).toWarnDev(
215
+ 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
216
+ 'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
217
+ ' componentWillReceiveProps\n\n' +
218
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
219
+ 'https://fb.me/react-async-component-lifecycle-hooks',
220
);
221
});
222
@@ -1238,9 +1328,7 @@ describe('ReactShallowRenderer', () => {
1328
1329
const shallowRenderer = createRenderer();
1330
expect(() => shallowRenderer.render(<ComponentWithWarnings />)).toWarnDev(
1241
- 'ComponentWithWarnings: Defines both componentWillReceiveProps() and static ' +
1242
- 'getDerivedStateFromProps() methods. We recommend using ' +
1243
- 'only getDerivedStateFromProps().',
1331
+ 'ComponentWithWarnings uses getDerivedStateFromProps() but also contains the following legacy lifecycles',
1332
);
1333
1334
// Should not log duplicate warning
packages/react/src/__tests__/createReactClassIntegration-test.js
+9
-1
@@ -509,7 +509,15 @@ describe('create-react-class-integration', () => {
509
510
expect(() => {
511
ReactDOM.render(<Component />, document.createElement('div'));
512
- }).toWarnDev('Defines both componentWillReceiveProps');
512
+ }).toWarnDev(
513
+ 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
514
+ 'Component uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
515
+ ' componentWillMount\n' +
516
+ ' componentWillReceiveProps\n' +
517
+ ' componentWillUpdate\n\n' +
518
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
519
+ 'https://fb.me/react-async-component-lifecycle-hooks',
520
+ );
521
ReactDOM.render(<Component foo={1} />, document.createElement('div'));
522
});
523