@samitouri / QOS-React / commits / e0d9b28999

[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