diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js
index c124cc55b6647..aa7fbf73c1a02 100644
--- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.internal.js
@@ -38,19 +38,21 @@ describe('ReactComponentLifeCycle', () => {
const container = document.createElement('div');
expect(() => ReactDOM.render(, container)).toWarnDev([
- 'Warning: MyComponent: componentWillMount() is deprecated and will be ' +
- 'removed in the next major version.',
+ 'componentWillMount is deprecated and will be removed in the next major version. ' +
+ 'Use componentDidMount instead. As a temporary workaround, ' +
+ 'you can rename to UNSAFE_componentWillMount.' +
+ '\n\nPlease update the following components: MyComponent',
+ 'componentWillReceiveProps is deprecated and will be removed in the next major version. ' +
+ 'Use static getDerivedStateFromProps instead.' +
+ '\n\nPlease update the following components: MyComponent',
+ 'componentWillUpdate is deprecated and will be removed in the next major version. ' +
+ 'Use componentDidUpdate instead. As a temporary workaround, ' +
+ 'you can rename to UNSAFE_componentWillUpdate.' +
+ '\n\nPlease update the following components: MyComponent',
]);
- expect(() => ReactDOM.render(, container)).toWarnDev([
- 'Warning: MyComponent: componentWillReceiveProps() is deprecated and ' +
- 'will be removed in the next major version.',
- 'Warning: MyComponent: componentWillUpdate() is deprecated and will be ' +
- 'removed in the next major version.',
- ]);
-
- // Dedupe check (instantiate and update)
+ // Dedupe check (update and instantiate new
+ ReactDOM.render(, container);
ReactDOM.render(, container);
- ReactDOM.render(, container);
});
});
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 8f8735ae8167f..ddd5a9cde2aa5 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -42,9 +42,6 @@ import {hasContextChanged} from './ReactFiberContext';
const fakeInternalInstance = {};
const isArray = Array.isArray;
-let didWarnAboutLegacyWillMount;
-let didWarnAboutLegacyWillReceiveProps;
-let didWarnAboutLegacyWillUpdate;
let didWarnAboutStateAssignmentForComponent;
let didWarnAboutUndefinedDerivedState;
let didWarnAboutUninitializedState;
@@ -52,11 +49,6 @@ let didWarnAboutWillReceivePropsAndDerivedState;
let warnOnInvalidCallback;
if (__DEV__) {
- if (warnAboutDeprecatedLifecycles) {
- didWarnAboutLegacyWillMount = {};
- didWarnAboutLegacyWillReceiveProps = {};
- didWarnAboutLegacyWillUpdate = {};
- }
didWarnAboutStateAssignmentForComponent = {};
didWarnAboutUndefinedDerivedState = {};
didWarnAboutUninitializedState = {};
@@ -462,25 +454,6 @@ export default function(
const oldState = instance.state;
if (typeof instance.componentWillMount === 'function') {
- if (__DEV__) {
- if (warnAboutDeprecatedLifecycles) {
- const componentName = getComponentName(workInProgress) || 'Component';
- if (!didWarnAboutLegacyWillMount[componentName]) {
- warning(
- false,
- '%s: componentWillMount() is deprecated and will be ' +
- 'removed in the next major version. Read about the motivations ' +
- 'behind this change: ' +
- 'https://fb.me/react-async-component-lifecycle-hooks' +
- '\n\n' +
- 'As a temporary workaround, you can rename to ' +
- 'UNSAFE_componentWillMount instead.',
- componentName,
- );
- didWarnAboutLegacyWillMount[componentName] = true;
- }
- }
- }
instance.componentWillMount();
} else {
instance.UNSAFE_componentWillMount();
@@ -510,27 +483,6 @@ export default function(
) {
const oldState = instance.state;
if (typeof instance.componentWillReceiveProps === 'function') {
- if (__DEV__) {
- if (warnAboutDeprecatedLifecycles) {
- const componentName = getComponentName(workInProgress) || 'Component';
- if (!didWarnAboutLegacyWillReceiveProps[componentName]) {
- warning(
- false,
- '%s: componentWillReceiveProps() is deprecated and ' +
- 'will be removed in the next major version. Use ' +
- 'static getDerivedStateFromProps() instead. Read about the ' +
- 'motivations behind this change: ' +
- 'https://fb.me/react-async-component-lifecycle-hooks' +
- '\n\n' +
- 'As a temporary workaround, you can rename to ' +
- 'UNSAFE_componentWillReceiveProps instead.',
- componentName,
- );
- didWarnAboutLegacyWillReceiveProps[componentName] = true;
- }
- }
- }
-
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
instance.componentWillReceiveProps(newProps, newContext);
stopPhaseTimer();
@@ -652,7 +604,14 @@ export default function(
if (__DEV__) {
if (workInProgress.internalContextTag & StrictMode) {
- ReactStrictModeWarnings.recordLifecycleWarnings(
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
+ workInProgress,
+ instance,
+ );
+ }
+
+ if (warnAboutDeprecatedLifecycles) {
+ ReactStrictModeWarnings.recordDeprecationWarnings(
workInProgress,
instance,
);
@@ -893,27 +852,6 @@ export default function(
typeof instance.componentWillUpdate === 'function'
) {
if (typeof instance.componentWillUpdate === 'function') {
- if (__DEV__) {
- if (warnAboutDeprecatedLifecycles) {
- const componentName =
- getComponentName(workInProgress) || 'Component';
- if (!didWarnAboutLegacyWillUpdate[componentName]) {
- warning(
- false,
- '%s: componentWillUpdate() is deprecated and will be ' +
- 'removed in the next major version. Read about the motivations ' +
- 'behind this change: ' +
- 'https://fb.me/react-async-component-lifecycle-hooks' +
- '\n\n' +
- 'As a temporary workaround, you can rename to ' +
- 'UNSAFE_componentWillUpdate instead.',
- componentName,
- );
- didWarnAboutLegacyWillUpdate[componentName] = true;
- }
- }
- }
-
startPhaseTimer(workInProgress, 'componentWillUpdate');
instance.componentWillUpdate(newProps, newState, newContext);
stopPhaseTimer();
diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js
index a2d0a5d354d59..77c967eb85399 100644
--- a/packages/react-reconciler/src/ReactFiberScheduler.js
+++ b/packages/react-reconciler/src/ReactFiberScheduler.js
@@ -34,7 +34,10 @@ import {
HostPortal,
ClassComponent,
} from 'shared/ReactTypeOfWork';
-import {enableUserTimingAPI} from 'shared/ReactFeatureFlags';
+import {
+ enableUserTimingAPI,
+ warnAboutDeprecatedLifecycles,
+} from 'shared/ReactFeatureFlags';
import getComponentName from 'shared/getComponentName';
import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';
@@ -312,7 +315,11 @@ export default function(
function commitAllLifeCycles() {
if (__DEV__) {
- ReactStrictModeWarnings.flushPendingAsyncWarnings();
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
+
+ if (warnAboutDeprecatedLifecycles) {
+ ReactStrictModeWarnings.flushPendingDeprecationWarnings();
+ }
}
while (nextEffect !== null) {
diff --git a/packages/react-reconciler/src/ReactStrictModeWarnings.js b/packages/react-reconciler/src/ReactStrictModeWarnings.js
index 2249d677f90d9..fb1b15d3d1618 100644
--- a/packages/react-reconciler/src/ReactStrictModeWarnings.js
+++ b/packages/react-reconciler/src/ReactStrictModeWarnings.js
@@ -23,8 +23,10 @@ type FiberToLifecycleMap = Map;
const ReactStrictModeWarnings = {
discardPendingWarnings(): void {},
- flushPendingAsyncWarnings(): void {},
- recordLifecycleWarnings(fiber: Fiber, instance: any): void {},
+ flushPendingDeprecationWarnings(): void {},
+ flushPendingUnsafeLifecycleWarnings(): void {},
+ recordDeprecationWarnings(fiber: Fiber, instance: any): void {},
+ recordUnsafeLifecycleWarnings(fiber: Fiber, instance: any): void {},
};
if (__DEV__) {
@@ -34,17 +36,24 @@ if (__DEV__) {
UNSAFE_componentWillUpdate: 'componentDidUpdate',
};
- let pendingWarningsMap: FiberToLifecycleMap = new Map();
+ let pendingComponentWillMountWarnings: Array = [];
+ let pendingComponentWillReceivePropsWarnings: Array = [];
+ let pendingComponentWillUpdateWarnings: Array = [];
+ let pendingUnsafeLifecycleWarnings: FiberToLifecycleMap = new Map();
// Tracks components we have already warned about.
- const didWarnSet = new Set();
+ const didWarnAboutDeprecatedLifecycles = new Set();
+ const didWarnAboutUnsafeLifecycles = new Set();
ReactStrictModeWarnings.discardPendingWarnings = () => {
- pendingWarningsMap = new Map();
+ pendingComponentWillMountWarnings = [];
+ pendingComponentWillReceivePropsWarnings = [];
+ pendingComponentWillUpdateWarnings = [];
+ pendingUnsafeLifecycleWarnings = new Map();
};
- ReactStrictModeWarnings.flushPendingAsyncWarnings = () => {
- ((pendingWarningsMap: any): FiberToLifecycleMap).forEach(
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = () => {
+ ((pendingUnsafeLifecycleWarnings: any): FiberToLifecycleMap).forEach(
(lifecycleWarningsMap, strictRoot) => {
const lifecyclesWarningMesages = [];
@@ -54,7 +63,7 @@ if (__DEV__) {
const componentNames = new Set();
lifecycleWarnings.forEach(fiber => {
componentNames.add(getComponentName(fiber) || 'Component');
- didWarnSet.add(fiber.type);
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
});
const formatted = lifecycle.replace('UNSAFE_', '');
@@ -88,7 +97,7 @@ if (__DEV__) {
},
);
- pendingWarningsMap = new Map();
+ pendingUnsafeLifecycleWarnings = new Map();
};
const getStrictRoot = (fiber: Fiber): Fiber => {
@@ -105,7 +114,103 @@ if (__DEV__) {
return maybeStrictRoot;
};
- ReactStrictModeWarnings.recordLifecycleWarnings = (
+ ReactStrictModeWarnings.flushPendingDeprecationWarnings = () => {
+ if (pendingComponentWillMountWarnings.length > 0) {
+ const uniqueNames = new Set();
+ pendingComponentWillMountWarnings.forEach(fiber => {
+ uniqueNames.add(getComponentName(fiber) || 'Component');
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ const sortedNames = Array.from(uniqueNames)
+ .sort()
+ .join(', ');
+
+ warning(
+ false,
+ 'componentWillMount is deprecated and will be removed in the next major version. ' +
+ 'Use componentDidMount instead. As a temporary workaround, ' +
+ 'you can rename to UNSAFE_componentWillMount.' +
+ '\n\nPlease update the following components: %s' +
+ '\n\nLearn more about this warning here:' +
+ '\nhttps://fb.me/react-async-component-lifecycle-hooks',
+ sortedNames,
+ );
+
+ pendingComponentWillMountWarnings = [];
+ }
+
+ if (pendingComponentWillReceivePropsWarnings.length > 0) {
+ const uniqueNames = new Set();
+ pendingComponentWillReceivePropsWarnings.forEach(fiber => {
+ uniqueNames.add(getComponentName(fiber) || 'Component');
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ const sortedNames = Array.from(uniqueNames)
+ .sort()
+ .join(', ');
+
+ warning(
+ false,
+ 'componentWillReceiveProps is deprecated and will be removed in the next major version. ' +
+ 'Use static getDerivedStateFromProps instead.' +
+ '\n\nPlease update the following components: %s' +
+ '\n\nLearn more about this warning here:' +
+ '\nhttps://fb.me/react-async-component-lifecycle-hooks',
+ sortedNames,
+ );
+
+ pendingComponentWillReceivePropsWarnings = [];
+ }
+
+ if (pendingComponentWillUpdateWarnings.length > 0) {
+ const uniqueNames = new Set();
+ pendingComponentWillUpdateWarnings.forEach(fiber => {
+ uniqueNames.add(getComponentName(fiber) || 'Component');
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ const sortedNames = Array.from(uniqueNames)
+ .sort()
+ .join(', ');
+
+ warning(
+ false,
+ 'componentWillUpdate is deprecated and will be removed in the next major version. ' +
+ 'Use componentDidUpdate instead. As a temporary workaround, ' +
+ 'you can rename to UNSAFE_componentWillUpdate.' +
+ '\n\nPlease update the following components: %s' +
+ '\n\nLearn more about this warning here:' +
+ '\nhttps://fb.me/react-async-component-lifecycle-hooks',
+ sortedNames,
+ );
+
+ pendingComponentWillUpdateWarnings = [];
+ }
+ };
+
+ ReactStrictModeWarnings.recordDeprecationWarnings = (
+ fiber: Fiber,
+ instance: any,
+ ) => {
+ // Dedup strategy: Warn once per component.
+ if (didWarnAboutDeprecatedLifecycles.has(fiber.type)) {
+ return;
+ }
+
+ if (typeof instance.componentWillMount === 'function') {
+ pendingComponentWillMountWarnings.push(fiber);
+ }
+ if (typeof instance.componentWillReceiveProps === 'function') {
+ pendingComponentWillReceivePropsWarnings.push(fiber);
+ }
+ if (typeof instance.componentWillUpdate === 'function') {
+ pendingComponentWillUpdateWarnings.push(fiber);
+ }
+ };
+
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = (
fiber: Fiber,
instance: any,
) => {
@@ -116,21 +221,21 @@ if (__DEV__) {
// are often vague and are likely to collide between 3rd party libraries.
// An expand property is probably okay to use here since it's DEV-only,
// and will only be set in the event of serious warnings.
- if (didWarnSet.has(fiber.type)) {
+ if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
return;
}
let warningsForRoot;
- if (!pendingWarningsMap.has(strictRoot)) {
+ if (!pendingUnsafeLifecycleWarnings.has(strictRoot)) {
warningsForRoot = {
UNSAFE_componentWillMount: [],
UNSAFE_componentWillReceiveProps: [],
UNSAFE_componentWillUpdate: [],
};
- pendingWarningsMap.set(strictRoot, warningsForRoot);
+ pendingUnsafeLifecycleWarnings.set(strictRoot, warningsForRoot);
} else {
- warningsForRoot = pendingWarningsMap.get(strictRoot);
+ warningsForRoot = pendingUnsafeLifecycleWarnings.get(strictRoot);
}
const unsafeLifecycles = [];
diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.internal.js b/packages/react/src/__tests__/createReactClassIntegration-test.internal.js
index e886b7867c94d..fae64dca9c673 100644
--- a/packages/react/src/__tests__/createReactClassIntegration-test.internal.js
+++ b/packages/react/src/__tests__/createReactClassIntegration-test.internal.js
@@ -95,14 +95,18 @@ describe('create-react-class-integration', () => {
'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
'clean up subscriptions and pending requests in componentWillUnmount ' +
'to prevent memory leaks.',
- 'Warning: MyComponent: componentWillMount() is deprecated and will be ' +
- 'removed in the next major version.',
+ 'componentWillMount is deprecated and will be removed in the next major version. ' +
+ 'Use componentDidMount instead. As a temporary workaround, ' +
+ 'you can rename to UNSAFE_componentWillMount.' +
+ '\n\nPlease update the following components: MyComponent',
+ 'componentWillUpdate is deprecated and will be removed in the next major version. ' +
+ 'Use componentDidUpdate instead. As a temporary workaround, ' +
+ 'you can rename to UNSAFE_componentWillUpdate.' +
+ '\n\nPlease update the following components: MyComponent',
]);
- expect(() => ReactDOM.render(, container)).toWarnDev(
- 'Warning: MyComponent: componentWillUpdate() is deprecated and will be ' +
- 'removed in the next major version.',
- );
+ // Dedupe
+ ReactDOM.render(, container);
ReactDOM.unmountComponentAtNode(container);
instance.log('after unmount');