Skip to content

Commit e0d9b28

Browse files
authored
[Fizz] Minor Fixes for Warning Parity (#21618)
1 parent 1b7b359 commit e0d9b28

File tree

4 files changed

+144
-28
lines changed

4 files changed

+144
-28
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ describe('ReactDOMServerLifecycles', () => {
115115
}
116116
}
117117

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+
);
119121
});
120122

121123
it('should update instance.state with value returned from getDerivedStateFromProps', () => {
@@ -279,8 +281,8 @@ describe('ReactDOMServerLifecycles', () => {
279281
}
280282
}
281283

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.',
284286
);
285287
});
286288

packages/react-dom/src/server/ReactPartialRenderer.js

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ const didWarnAboutModulePatternComponent = {};
194194
const didWarnAboutDeprecatedWillMount = {};
195195
const didWarnAboutUndefinedDerivedState = {};
196196
const didWarnAboutUninitializedState = {};
197+
const didWarnAboutLegacyLifecyclesAndDerivedState = {};
197198
const valuePropNames = ['value', 'defaultValue'];
198199
const newlineEatingTags = {
199200
listing: true,
@@ -475,6 +476,79 @@ function resolve(
475476
didWarnAboutUninitializedState[componentName] = true;
476477
}
477478
}
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+
}
478552
}
479553

480554
const partialState = Component.getDerivedStateFromProps.call(
@@ -575,32 +649,32 @@ function resolve(
575649
typeof inst.componentWillMount === 'function'
576650
) {
577651
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-
601652
// In order to support react-lifecycles-compat polyfilled components,
602653
// Unsafe lifecycles should not be invoked for any component with the new gDSFP.
603654
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+
604678
inst.componentWillMount();
605679
}
606680
}

packages/react-server/src/ReactFizzClassComponent.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@
1010
import {emptyContextObject} from './ReactFizzContext';
1111
import {readContext} from './ReactFizzNewContext';
1212

13-
import {disableLegacyContext} from 'shared/ReactFeatureFlags';
13+
import {
14+
disableLegacyContext,
15+
warnAboutDeprecatedLifecycles,
16+
} from 'shared/ReactFeatureFlags';
1417
import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap';
1518
import getComponentNameFromType from 'shared/getComponentNameFromType';
1619
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
1720
import isArray from 'shared/isArray';
1821

1922
const didWarnAboutNoopUpdateForComponent = {};
23+
const didWarnAboutDeprecatedWillMount = {};
2024

2125
let didWarnAboutUninitializedState;
2226
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
@@ -531,6 +535,28 @@ function callComponentWillMount(type, instance) {
531535
const oldState = instance.state;
532536

533537
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+
534560
instance.componentWillMount();
535561
}
536562
if (typeof instance.UNSAFE_componentWillMount === 'function') {

packages/react-server/src/ReactFizzServer.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1032,13 +1032,27 @@ function renderElement(
10321032
}
10331033
}
10341034

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+
}
10351049
invariant(
10361050
false,
10371051
'Element type is invalid: expected a string (for built-in ' +
10381052
'components) or a class/function (for composite components) ' +
10391053
'but got: %s.%s',
10401054
type == null ? type : typeof type,
1041-
'',
1055+
info,
10421056
);
10431057
}
10441058

0 commit comments

Comments
 (0)