[Fizz] Minor Fixes for Warning Parity (#21618)
Sebastian Markbåge committed
Jun 3, 2021 at 19:54 UTC
e0d9b289998c66d2e4fb75582b9e107054e4e0a4
4 files changed
+144
-28
packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js
+5
-3
@@ -115,7 +115,9 @@ describe('ReactDOMServerLifecycles', () => {
115
}
116
}
117
118
- ReactDOMServer.renderToString(<Component />);
118
+ expect(() => ReactDOMServer.renderToString(<Component />)).toErrorDev(
119
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
120
+ );
121
});
122
123
it('should update instance.state with value returned from getDerivedStateFromProps', () => {
@@ -279,8 +281,8 @@ describe('ReactDOMServerLifecycles', () => {
281
}
282
}
283
282
- expect(() => ReactDOMServer.renderToString(<Component />)).toWarnDev(
283
- 'componentWillMount has been renamed',
284
+ expect(() => ReactDOMServer.renderToString(<Component />)).toErrorDev(
285
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
286
);
287
});
288
packages/react-dom/src/server/ReactPartialRenderer.js
+97
-23
@@ -194,6 +194,7 @@ const didWarnAboutModulePatternComponent = {};
194
const didWarnAboutDeprecatedWillMount = {};
195
const didWarnAboutUndefinedDerivedState = {};
196
const didWarnAboutUninitializedState = {};
197
+const didWarnAboutLegacyLifecyclesAndDerivedState = {};
198
const valuePropNames = ['value', 'defaultValue'];
199
const newlineEatingTags = {
200
listing: true,
@@ -475,6 +476,79 @@ function resolve(
476
didWarnAboutUninitializedState[componentName] = true;
477
}
478
}
479
+
480
+ // If new component APIs are defined, "unsafe" lifecycles won't be called.
481
+ // Warn about these lifecycles if they are present.
482
+ // Don't warn about react-lifecycles-compat polyfilled methods though.
483
+ if (
484
+ typeof Component.getDerivedStateFromProps === 'function' ||
485
+ typeof inst.getSnapshotBeforeUpdate === 'function'
486
+ ) {
487
+ let foundWillMountName = null;
488
+ let foundWillReceivePropsName = null;
489
+ let foundWillUpdateName = null;
490
+ if (
491
+ typeof inst.componentWillMount === 'function' &&
492
+ inst.componentWillMount.__suppressDeprecationWarning !== true
493
+ ) {
494
+ foundWillMountName = 'componentWillMount';
495
+ } else if (typeof inst.UNSAFE_componentWillMount === 'function') {
496
+ foundWillMountName = 'UNSAFE_componentWillMount';
497
+ }
498
+ if (
499
+ typeof inst.componentWillReceiveProps === 'function' &&
500
+ inst.componentWillReceiveProps.__suppressDeprecationWarning !==
501
+ true
502
+ ) {
503
+ foundWillReceivePropsName = 'componentWillReceiveProps';
504
+ } else if (
505
+ typeof inst.UNSAFE_componentWillReceiveProps === 'function'
506
+ ) {
507
+ foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
508
+ }
509
+ if (
510
+ typeof inst.componentWillUpdate === 'function' &&
511
+ inst.componentWillUpdate.__suppressDeprecationWarning !== true
512
+ ) {
513
+ foundWillUpdateName = 'componentWillUpdate';
514
+ } else if (typeof inst.UNSAFE_componentWillUpdate === 'function') {
515
+ foundWillUpdateName = 'UNSAFE_componentWillUpdate';
516
+ }
517
+ if (
518
+ foundWillMountName !== null ||
519
+ foundWillReceivePropsName !== null ||
520
+ foundWillUpdateName !== null
521
+ ) {
522
+ const componentName =
523
+ getComponentNameFromType(Component) || 'Component';
524
+ const newApiName =
525
+ typeof Component.getDerivedStateFromProps === 'function'
526
+ ? 'getDerivedStateFromProps()'
527
+ : 'getSnapshotBeforeUpdate()';
528
+ if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
529
+ didWarnAboutLegacyLifecyclesAndDerivedState[
530
+ componentName
531
+ ] = true;
532
+ console.error(
533
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
534
+ '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
535
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
536
+ 'https://reactjs.org/link/unsafe-component-lifecycles',
537
+ componentName,
538
+ newApiName,
539
+ foundWillMountName !== null
540
+ ? `\n ${foundWillMountName}`
541
+ : '',
542
+ foundWillReceivePropsName !== null
543
+ ? `\n ${foundWillReceivePropsName}`
544
+ : '',
545
+ foundWillUpdateName !== null
546
+ ? `\n ${foundWillUpdateName}`
547
+ : '',
548
+ );
549
+ }
550
+ }
551
+ }
552
}
553
554
const partialState = Component.getDerivedStateFromProps.call(
@@ -575,32 +649,32 @@ function resolve(
649
typeof inst.componentWillMount === 'function'
650
) {
651
if (typeof inst.componentWillMount === 'function') {
578
- if (__DEV__) {
579
- if (
580
- warnAboutDeprecatedLifecycles &&
581
- inst.componentWillMount.__suppressDeprecationWarning !== true
582
- ) {
583
- const componentName =
584
- getComponentNameFromType(Component) || 'Unknown';
585
-
586
- if (!didWarnAboutDeprecatedWillMount[componentName]) {
587
- console.warn(
588
- // keep this warning in sync with ReactStrictModeWarning.js
589
- 'componentWillMount has been renamed, and is not recommended for use. ' +
590
- 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' +
591
- '* Move code from componentWillMount to componentDidMount (preferred in most cases) ' +
592
- 'or the constructor.\n' +
593
- '\nPlease update the following components: %s',
594
- componentName,
595
- );
596
- didWarnAboutDeprecatedWillMount[componentName] = true;
597
- }
598
- }
599
- }
600
-
652
// In order to support react-lifecycles-compat polyfilled components,
653
// Unsafe lifecycles should not be invoked for any component with the new gDSFP.
654
if (typeof Component.getDerivedStateFromProps !== 'function') {
655
+ if (__DEV__) {
656
+ if (
657
+ warnAboutDeprecatedLifecycles &&
658
+ inst.componentWillMount.__suppressDeprecationWarning !== true
659
+ ) {
660
+ const componentName =
661
+ getComponentNameFromType(Component) || 'Unknown';
662
+
663
+ if (!didWarnAboutDeprecatedWillMount[componentName]) {
664
+ console.warn(
665
+ // keep this warning in sync with ReactStrictModeWarning.js
666
+ 'componentWillMount has been renamed, and is not recommended for use. ' +
667
+ 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' +
668
+ '* Move code from componentWillMount to componentDidMount (preferred in most cases) ' +
669
+ 'or the constructor.\n' +
670
+ '\nPlease update the following components: %s',
671
+ componentName,
672
+ );
673
+ didWarnAboutDeprecatedWillMount[componentName] = true;
674
+ }
675
+ }
676
+ }
677
+
678
inst.componentWillMount();
679
}
680
}
packages/react-server/src/ReactFizzClassComponent.js
+27
-1
@@ -10,13 +10,17 @@
10
import {emptyContextObject} from './ReactFizzContext';
11
import {readContext} from './ReactFizzNewContext';
12
13
-import {disableLegacyContext} from 'shared/ReactFeatureFlags';
13
+import {
14
+ disableLegacyContext,
15
+ warnAboutDeprecatedLifecycles,
16
+} from 'shared/ReactFeatureFlags';
17
import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap';
18
import getComponentNameFromType from 'shared/getComponentNameFromType';
19
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
20
import isArray from 'shared/isArray';
21
22
const didWarnAboutNoopUpdateForComponent = {};
23
+const didWarnAboutDeprecatedWillMount = {};
24
25
let didWarnAboutUninitializedState;
26
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
@@ -531,6 +535,28 @@ function callComponentWillMount(type, instance) {
535
const oldState = instance.state;
536
537
if (typeof instance.componentWillMount === 'function') {
538
+ if (__DEV__) {
539
+ if (
540
+ warnAboutDeprecatedLifecycles &&
541
+ instance.componentWillMount.__suppressDeprecationWarning !== true
542
+ ) {
543
+ const componentName = getComponentNameFromType(type) || 'Unknown';
544
+
545
+ if (!didWarnAboutDeprecatedWillMount[componentName]) {
546
+ console.warn(
547
+ // keep this warning in sync with ReactStrictModeWarning.js
548
+ 'componentWillMount has been renamed, and is not recommended for use. ' +
549
+ 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' +
550
+ '* Move code from componentWillMount to componentDidMount (preferred in most cases) ' +
551
+ 'or the constructor.\n' +
552
+ '\nPlease update the following components: %s',
553
+ componentName,
554
+ );
555
+ didWarnAboutDeprecatedWillMount[componentName] = true;
556
+ }
557
+ }
558
+ }
559
+
560
instance.componentWillMount();
561
}
562
if (typeof instance.UNSAFE_componentWillMount === 'function') {
packages/react-server/src/ReactFizzServer.js
+15
-1
@@ -1032,13 +1032,27 @@ function renderElement(
1032
}
1033
}
1034
1035
+ let info = '';
1036
+ if (__DEV__) {
1037
+ if (
1038
+ type === undefined ||
1039
+ (typeof type === 'object' &&
1040
+ type !== null &&
1041
+ Object.keys(type).length === 0)
1042
+ ) {
1043
+ info +=
1044
+ ' You likely forgot to export your component from the file ' +
1045
+ "it's defined in, or you might have mixed up default and " +
1046
+ 'named imports.';
1047
+ }
1048
+ }
1049
invariant(
1050
false,
1051
'Element type is invalid: expected a string (for built-in ' +
1052
'components) or a class/function (for composite components) ' +
1053
'but got: %s.%s',
1054
type == null ? type : typeof type,
1041
- '',
1055
+ info,
1056
);
1057
}
1058