@samitouri / QOS-React / commits / c1308adb4b

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