diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
index fd286f1e68c8e..3c6d08110324a 100644
--- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
@@ -709,9 +709,9 @@ describe('ReactComponentLifeCycle', () => {
);
}).toLowPriorityWarnDev(
[
- 'componentWillMount is deprecated',
- 'componentWillReceiveProps is deprecated',
- 'componentWillUpdate is deprecated',
+ 'componentWillMount has been renamed',
+ 'componentWillReceiveProps has been renamed',
+ 'componentWillUpdate has been renamed',
],
{withoutStack: true},
);
@@ -748,9 +748,9 @@ describe('ReactComponentLifeCycle', () => {
);
}).toLowPriorityWarnDev(
[
- 'componentWillMount is deprecated',
- 'componentWillReceiveProps is deprecated',
- 'componentWillUpdate is deprecated',
+ 'componentWillMount has been renamed',
+ 'componentWillReceiveProps has been renamed',
+ 'componentWillUpdate has been renamed',
],
{withoutStack: true},
);
@@ -815,7 +815,10 @@ describe('ReactComponentLifeCycle', () => {
{withoutStack: true},
);
}).toLowPriorityWarnDev(
- ['componentWillMount is deprecated', 'componentWillUpdate is deprecated'],
+ [
+ 'componentWillMount has been renamed',
+ 'componentWillUpdate has been renamed',
+ ],
{withoutStack: true},
);
@@ -863,7 +866,7 @@ describe('ReactComponentLifeCycle', () => {
'https://fb.me/react-async-component-lifecycle-hooks',
{withoutStack: true},
);
- }).toLowPriorityWarnDev(['componentWillMount is deprecated'], {
+ }).toLowPriorityWarnDev(['componentWillMount has been renamed'], {
withoutStack: true,
});
@@ -887,7 +890,7 @@ describe('ReactComponentLifeCycle', () => {
'https://fb.me/react-async-component-lifecycle-hooks',
{withoutStack: true},
);
- }).toLowPriorityWarnDev(['componentWillReceiveProps is deprecated'], {
+ }).toLowPriorityWarnDev(['componentWillReceiveProps has been renamed'], {
withoutStack: true,
});
});
@@ -921,7 +924,10 @@ describe('ReactComponentLifeCycle', () => {
{withoutStack: true},
);
}).toLowPriorityWarnDev(
- ['componentWillMount is deprecated', 'componentWillUpdate is deprecated'],
+ [
+ 'componentWillMount has been renamed',
+ 'componentWillUpdate has been renamed',
+ ],
{withoutStack: true},
);
@@ -967,7 +973,7 @@ describe('ReactComponentLifeCycle', () => {
'https://fb.me/react-async-component-lifecycle-hooks',
{withoutStack: true},
);
- }).toLowPriorityWarnDev(['componentWillMount is deprecated'], {
+ }).toLowPriorityWarnDev(['componentWillMount has been renamed'], {
withoutStack: true,
});
@@ -990,7 +996,7 @@ describe('ReactComponentLifeCycle', () => {
'https://fb.me/react-async-component-lifecycle-hooks',
{withoutStack: true},
);
- }).toLowPriorityWarnDev(['componentWillReceiveProps is deprecated'], {
+ }).toLowPriorityWarnDev(['componentWillReceiveProps has been renamed'], {
withoutStack: true,
});
});
@@ -1130,9 +1136,9 @@ describe('ReactComponentLifeCycle', () => {
ReactDOM.render(, div),
).toLowPriorityWarnDev(
[
- 'componentWillMount is deprecated',
- 'componentWillReceiveProps is deprecated',
- 'componentWillUpdate is deprecated',
+ 'componentWillMount has been renamed',
+ 'componentWillReceiveProps has been renamed',
+ 'componentWillUpdate has been renamed',
],
{withoutStack: true},
);
@@ -1403,17 +1409,9 @@ describe('ReactComponentLifeCycle', () => {
ReactDOM.render(, container),
).toLowPriorityWarnDev(
[
- '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',
+ 'componentWillMount has been renamed',
+ 'componentWillReceiveProps has been renamed',
+ 'componentWillUpdate has been renamed',
],
{withoutStack: true},
);
diff --git a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js
index 76230a3230350..4aed0e0246a69 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerLifecycles-test.js
@@ -229,7 +229,7 @@ describe('ReactDOMServerLifecycles', () => {
expect(() =>
ReactDOMServer.renderToString(),
- ).toLowPriorityWarnDev('componentWillMount() is deprecated', {
+ ).toLowPriorityWarnDev('componentWillMount has been renamed', {
withoutStack: true,
});
expect(log).toEqual(['componentWillMount', 'UNSAFE_componentWillMount']);
@@ -286,10 +286,9 @@ describe('ReactDOMServerLifecycles', () => {
expect(() =>
ReactDOMServer.renderToString(),
- ).toLowPriorityWarnDev(
- 'Component: componentWillMount() is deprecated and will be removed in the next major version.',
- {withoutStack: true},
- );
+ ).toLowPriorityWarnDev('componentWillMount has been renamed', {
+ withoutStack: true,
+ });
});
it('should warn about deprecated lifecycle hooks', () => {
@@ -302,11 +301,9 @@ describe('ReactDOMServerLifecycles', () => {
expect(() =>
ReactDOMServer.renderToString(),
- ).toLowPriorityWarnDev(
- 'Warning: Component: componentWillMount() is deprecated and will be removed ' +
- 'in the next major version.',
- {withoutStack: true},
- );
+ ).toLowPriorityWarnDev('componentWillMount has been renamed', {
+ withoutStack: true,
+ });
// De-duped
ReactDOMServer.renderToString();
diff --git a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
index 38d300debd14e..d093a11bf8ab2 100644
--- a/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
+++ b/packages/react-dom/src/__tests__/ReactServerRenderingHydration-test.js
@@ -362,20 +362,16 @@ describe('ReactDOMServerHydration', () => {
const element = document.createElement('div');
expect(() => {
element.innerHTML = ReactDOMServer.renderToString(markup);
- }).toLowPriorityWarnDev(
- ['componentWillMount() is deprecated and will be removed'],
- {withoutStack: true},
- );
+ }).toLowPriorityWarnDev(['componentWillMount has been renamed'], {
+ withoutStack: true,
+ });
expect(element.textContent).toBe('Hi');
expect(() => {
- expect(() => ReactDOM.hydrate(markup, element)).toWarnDev(
- 'Please update the following components to use componentDidMount instead: ComponentWithWarning',
- );
- }).toLowPriorityWarnDev(
- ['componentWillMount is deprecated and will be removed'],
- {withoutStack: true},
- );
+ ReactDOM.hydrate(markup, element);
+ }).toLowPriorityWarnDev(['componentWillMount has been renamed'], {
+ withoutStack: true,
+ });
expect(element.textContent).toBe('Hi');
});
diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js
index fdc7e17b3aa1a..7e0683a153a7b 100644
--- a/packages/react-dom/src/server/ReactPartialRenderer.js
+++ b/packages/react-dom/src/server/ReactPartialRenderer.js
@@ -572,13 +572,12 @@ function resolve(
if (!didWarnAboutDeprecatedWillMount[componentName]) {
lowPriorityWarning(
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.',
+ // keep this warning in sync with ReactStrictModeWarning.js
+ 'componentWillMount has been renamed, and is not recommended for use. ' +
+ 'See https://fb.me/react-async-component-lifecycle-hooks for details.\n\n' +
+ '* Move component logic from componentWillMount into componentDidMount (preferred in most cases) ' +
+ 'or the constructor.\n' +
+ '\nPlease update the following components: %s',
componentName,
);
didWarnAboutDeprecatedWillMount[componentName] = true;
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 369b01dbca350..db35d7fe714b0 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -806,11 +806,6 @@ function mountClassInstance(
}
if (workInProgress.mode & StrictMode) {
- ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
- workInProgress,
- instance,
- );
-
ReactStrictModeWarnings.recordLegacyContextWarning(
workInProgress,
instance,
diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.js b/packages/react-reconciler/src/ReactFiberWorkLoop.js
index b05e42a5512d9..7c487d177553d 100644
--- a/packages/react-reconciler/src/ReactFiberWorkLoop.js
+++ b/packages/react-reconciler/src/ReactFiberWorkLoop.js
@@ -2247,7 +2247,6 @@ function checkForNestedUpdates() {
function flushRenderPhaseStrictModeWarningsInDEV() {
if (__DEV__) {
- ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
ReactStrictModeWarnings.flushLegacyContextWarning();
if (warnAboutDeprecatedLifecycles) {
diff --git a/packages/react-reconciler/src/ReactStrictModeWarnings.js b/packages/react-reconciler/src/ReactStrictModeWarnings.js
index 143e7ebd4a7b9..caf989195a5de 100644
--- a/packages/react-reconciler/src/ReactStrictModeWarnings.js
+++ b/packages/react-reconciler/src/ReactStrictModeWarnings.js
@@ -16,42 +16,31 @@ import {StrictMode} from './ReactTypeOfMode';
import lowPriorityWarning from 'shared/lowPriorityWarning';
import warningWithoutStack from 'shared/warningWithoutStack';
-type LIFECYCLE =
- | 'UNSAFE_componentWillMount'
- | 'UNSAFE_componentWillReceiveProps'
- | 'UNSAFE_componentWillUpdate';
-type LifecycleToComponentsMap = {[lifecycle: LIFECYCLE]: Array};
-type FiberToLifecycleMap = Map;
type FiberArray = Array;
type FiberToFiberComponentsMap = Map;
const ReactStrictModeWarnings = {
- discardPendingWarnings(): void {},
- flushPendingDeprecationWarnings(): void {},
- flushPendingUnsafeLifecycleWarnings(): void {},
recordDeprecationWarnings(fiber: Fiber, instance: any): void {},
- recordUnsafeLifecycleWarnings(fiber: Fiber, instance: any): void {},
+ flushPendingDeprecationWarnings(): void {},
recordLegacyContextWarning(fiber: Fiber, instance: any): void {},
flushLegacyContextWarning(): void {},
+ discardPendingWarnings(): void {},
};
if (__DEV__) {
- const LIFECYCLE_SUGGESTIONS = {
- UNSAFE_componentWillMount: 'componentDidMount',
- UNSAFE_componentWillReceiveProps: 'static getDerivedStateFromProps',
- UNSAFE_componentWillUpdate: 'componentDidUpdate',
- };
+ const findStrictRoot = (fiber: Fiber): Fiber | null => {
+ let maybeStrictRoot = null;
- let pendingComponentWillMountWarnings: Array = [];
- let pendingComponentWillReceivePropsWarnings: Array = [];
- let pendingComponentWillUpdateWarnings: Array = [];
- let pendingUnsafeLifecycleWarnings: FiberToLifecycleMap = new Map();
- let pendingLegacyContextWarning: FiberToFiberComponentsMap = new Map();
+ let node = fiber;
+ while (node !== null) {
+ if (node.mode & StrictMode) {
+ maybeStrictRoot = node;
+ }
+ node = node.return;
+ }
- // Tracks components we have already warned about.
- const didWarnAboutDeprecatedLifecycles = new Set();
- const didWarnAboutUnsafeLifecycles = new Set();
- const didWarnAboutLegacyContext = new Set();
+ return maybeStrictRoot;
+ };
const setToSortedString = set => {
const array = [];
@@ -61,243 +50,256 @@ if (__DEV__) {
return array.sort().join(', ');
};
- ReactStrictModeWarnings.discardPendingWarnings = () => {
- pendingComponentWillMountWarnings = [];
- pendingComponentWillReceivePropsWarnings = [];
- pendingComponentWillUpdateWarnings = [];
- pendingUnsafeLifecycleWarnings = new Map();
- pendingLegacyContextWarning = new Map();
- };
+ let pendingComponentWillMountWarnings: Array = [];
+ let pendingUNSAFE_ComponentWillMountWarnings: Array = [];
+ let pendingComponentWillReceivePropsWarnings: Array = [];
+ let pendingUNSAFE_ComponentWillReceivePropsWarnings: Array = [];
+ let pendingComponentWillUpdateWarnings: Array = [];
+ let pendingUNSAFE_ComponentWillUpdateWarnings: Array = [];
- ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = () => {
- ((pendingUnsafeLifecycleWarnings: any): FiberToLifecycleMap).forEach(
- (lifecycleWarningsMap, strictRoot) => {
- const lifecyclesWarningMessages = [];
-
- Object.keys(lifecycleWarningsMap).forEach(lifecycle => {
- const lifecycleWarnings = lifecycleWarningsMap[lifecycle];
- if (lifecycleWarnings.length > 0) {
- const componentNames = new Set();
- lifecycleWarnings.forEach(fiber => {
- componentNames.add(getComponentName(fiber.type) || 'Component');
- didWarnAboutUnsafeLifecycles.add(fiber.type);
- });
-
- const formatted = lifecycle.replace('UNSAFE_', '');
- const suggestion = LIFECYCLE_SUGGESTIONS[lifecycle];
- const sortedComponentNames = setToSortedString(componentNames);
-
- lifecyclesWarningMessages.push(
- `${formatted}: Please update the following components to use ` +
- `${suggestion} instead: ${sortedComponentNames}`,
- );
- }
- });
+ // Tracks components we have already warned about.
+ const didWarnAboutDeprecatedLifecycles = new Set();
- if (lifecyclesWarningMessages.length > 0) {
- const strictRootComponentStack = getStackByFiberInDevAndProd(
- strictRoot,
- );
-
- warningWithoutStack(
- false,
- 'Unsafe lifecycle methods were found within a strict-mode tree:%s' +
- '\n\n%s' +
- '\n\nLearn more about this warning here:' +
- '\nhttps://fb.me/react-strict-mode-warnings',
- strictRootComponentStack,
- lifecyclesWarningMessages.join('\n\n'),
- );
- }
- },
- );
+ ReactStrictModeWarnings.recordDeprecationWarnings = (
+ fiber: Fiber,
+ instance: any,
+ ) => {
+ // Dedup strategy: Warn once per component.
+ if (didWarnAboutDeprecatedLifecycles.has(fiber.type)) {
+ return;
+ }
- pendingUnsafeLifecycleWarnings = new Map();
- };
+ if (
+ typeof instance.componentWillMount === 'function' &&
+ // Don't warn about react-lifecycles-compat polyfilled components.
+ instance.componentWillMount.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillMountWarnings.push(fiber);
+ }
- const findStrictRoot = (fiber: Fiber): Fiber | null => {
- let maybeStrictRoot = null;
+ if (
+ fiber.mode & StrictMode &&
+ typeof instance.UNSAFE_componentWillMount === 'function'
+ ) {
+ pendingUNSAFE_ComponentWillMountWarnings.push(fiber);
+ }
- let node = fiber;
- while (node !== null) {
- if (node.mode & StrictMode) {
- maybeStrictRoot = node;
- }
- node = node.return;
+ if (
+ typeof instance.componentWillReceiveProps === 'function' &&
+ instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillReceivePropsWarnings.push(fiber);
}
- return maybeStrictRoot;
+ if (
+ fiber.mode & StrictMode &&
+ typeof instance.UNSAFE_componentWillReceiveProps === 'function'
+ ) {
+ pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber);
+ }
+
+ if (
+ typeof instance.componentWillUpdate === 'function' &&
+ instance.componentWillUpdate.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillUpdateWarnings.push(fiber);
+ }
+
+ if (
+ fiber.mode & StrictMode &&
+ typeof instance.UNSAFE_componentWillUpdate === 'function'
+ ) {
+ pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber);
+ }
};
ReactStrictModeWarnings.flushPendingDeprecationWarnings = () => {
+ // We do an initial pass to gather component names
+ const componentWillMountUniqueNames = new Set();
if (pendingComponentWillMountWarnings.length > 0) {
- const uniqueNames = new Set();
pendingComponentWillMountWarnings.forEach(fiber => {
- uniqueNames.add(getComponentName(fiber.type) || 'Component');
+ componentWillMountUniqueNames.add(
+ getComponentName(fiber.type) || 'Component',
+ );
didWarnAboutDeprecatedLifecycles.add(fiber.type);
});
-
- const sortedNames = setToSortedString(uniqueNames);
-
- lowPriorityWarning(
- 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 = [];
}
+ const UNSAFE_componentWillMountUniqueNames = new Set();
+ if (pendingUNSAFE_ComponentWillMountWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillMountWarnings.forEach(fiber => {
+ UNSAFE_componentWillMountUniqueNames.add(
+ getComponentName(fiber.type) || 'Component',
+ );
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+ pendingUNSAFE_ComponentWillMountWarnings = [];
+ }
+
+ const componentWillReceivePropsUniqueNames = new Set();
if (pendingComponentWillReceivePropsWarnings.length > 0) {
- const uniqueNames = new Set();
pendingComponentWillReceivePropsWarnings.forEach(fiber => {
- uniqueNames.add(getComponentName(fiber.type) || 'Component');
+ componentWillReceivePropsUniqueNames.add(
+ getComponentName(fiber.type) || 'Component',
+ );
didWarnAboutDeprecatedLifecycles.add(fiber.type);
});
- const sortedNames = setToSortedString(uniqueNames);
+ pendingComponentWillReceivePropsWarnings = [];
+ }
- lowPriorityWarning(
- 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,
- );
+ const UNSAFE_componentWillReceivePropsUniqueNames = new Set();
+ if (pendingUNSAFE_ComponentWillReceivePropsWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillReceivePropsWarnings.forEach(fiber => {
+ UNSAFE_componentWillReceivePropsUniqueNames.add(
+ getComponentName(fiber.type) || 'Component',
+ );
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
- pendingComponentWillReceivePropsWarnings = [];
+ pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
}
+ const componentWillUpdateUniqueNames = new Set();
if (pendingComponentWillUpdateWarnings.length > 0) {
- const uniqueNames = new Set();
pendingComponentWillUpdateWarnings.forEach(fiber => {
- uniqueNames.add(getComponentName(fiber.type) || 'Component');
+ componentWillUpdateUniqueNames.add(
+ getComponentName(fiber.type) || 'Component',
+ );
didWarnAboutDeprecatedLifecycles.add(fiber.type);
});
- const sortedNames = setToSortedString(uniqueNames);
-
- lowPriorityWarning(
- 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;
- }
+ const UNSAFE_componentWillUpdateUniqueNames = new Set();
+ if (pendingUNSAFE_ComponentWillUpdateWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillUpdateWarnings.forEach(fiber => {
+ UNSAFE_componentWillUpdateUniqueNames.add(
+ getComponentName(fiber.type) || 'Component',
+ );
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
- // Don't warn about react-lifecycles-compat polyfilled components.
- if (
- typeof instance.componentWillMount === 'function' &&
- instance.componentWillMount.__suppressDeprecationWarning !== true
- ) {
- pendingComponentWillMountWarnings.push(fiber);
- }
- if (
- typeof instance.componentWillReceiveProps === 'function' &&
- instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
- ) {
- pendingComponentWillReceivePropsWarnings.push(fiber);
- }
- if (
- typeof instance.componentWillUpdate === 'function' &&
- instance.componentWillUpdate.__suppressDeprecationWarning !== true
- ) {
- pendingComponentWillUpdateWarnings.push(fiber);
+ pendingUNSAFE_ComponentWillUpdateWarnings = [];
}
- };
- ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = (
- fiber: Fiber,
- instance: any,
- ) => {
- const strictRoot = findStrictRoot(fiber);
- if (strictRoot === null) {
+ // Finally, we flush all the warnings
+ // UNSAFE_ ones before the deprecated ones, since they'll be 'louder'
+ if (UNSAFE_componentWillMountUniqueNames.size > 0) {
+ const sortedNames = setToSortedString(
+ UNSAFE_componentWillMountUniqueNames,
+ );
warningWithoutStack(
false,
- 'Expected to find a StrictMode component in a strict mode tree. ' +
- 'This error is likely caused by a bug in React. Please file an issue.',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. ' +
+ 'See https://fb.me/react-async-component-lifecycle-hooks for details.\n\n' +
+ '* Move component logic from UNSAFE_componentWillMount into componentDidMount (preferred in most cases) ' +
+ 'or the constructor.\n' +
+ '\nPlease update the following components: %s',
+ sortedNames,
);
- return;
}
- // Dedup strategy: Warn once per component.
- // This is difficult to track any other way since component names
- // 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 (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
- return;
+ if (UNSAFE_componentWillReceivePropsUniqueNames.size > 0) {
+ const sortedNames = setToSortedString(
+ UNSAFE_componentWillReceivePropsUniqueNames,
+ );
+ warningWithoutStack(
+ false,
+ 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended ' +
+ 'and may indicate bugs in your code. ' +
+ 'See https://fb.me/react-async-component-lifecycle-hooks for details.\n\n' +
+ '* Move data fetching logic or side effects from UNSAFE_componentWillReceiveProps ' +
+ 'into componentDidUpdate.\n' +
+ "* If you're updating state whenever props change, " +
+ 'move this logic into static getDerivedStateFromProps, or refactor your ' +
+ 'code to use memoization techniques. ' +
+ 'Learn more at: https://fb.me/react-derived-state\n' +
+ '\nPlease update the following components: %s',
+ sortedNames,
+ );
}
- let warningsForRoot;
- if (!pendingUnsafeLifecycleWarnings.has(strictRoot)) {
- warningsForRoot = {
- UNSAFE_componentWillMount: [],
- UNSAFE_componentWillReceiveProps: [],
- UNSAFE_componentWillUpdate: [],
- };
-
- pendingUnsafeLifecycleWarnings.set(strictRoot, warningsForRoot);
- } else {
- warningsForRoot = pendingUnsafeLifecycleWarnings.get(strictRoot);
+ if (UNSAFE_componentWillUpdateUniqueNames.size > 0) {
+ const sortedNames = setToSortedString(
+ UNSAFE_componentWillUpdateUniqueNames,
+ );
+ warningWithoutStack(
+ false,
+ 'Using UNSAFE_componentWillUpdate in strict mode is not recommended ' +
+ 'and may indicate bugs in your code. ' +
+ 'See https://fb.me/react-async-component-lifecycle-hooks for details.\n\n' +
+ '* Move data fetching logic or side effects from UNSAFE_componentWillUpdate into componentDidUpdate.\n' +
+ '\nPlease update the following components: %s',
+ sortedNames,
+ );
}
- const unsafeLifecycles = [];
- if (
- (typeof instance.componentWillMount === 'function' &&
- instance.componentWillMount.__suppressDeprecationWarning !== true) ||
- typeof instance.UNSAFE_componentWillMount === 'function'
- ) {
- unsafeLifecycles.push('UNSAFE_componentWillMount');
- }
- if (
- (typeof instance.componentWillReceiveProps === 'function' &&
- instance.componentWillReceiveProps.__suppressDeprecationWarning !==
- true) ||
- typeof instance.UNSAFE_componentWillReceiveProps === 'function'
- ) {
- unsafeLifecycles.push('UNSAFE_componentWillReceiveProps');
+ if (componentWillMountUniqueNames.size > 0) {
+ const sortedNames = setToSortedString(componentWillMountUniqueNames);
+
+ lowPriorityWarning(
+ false,
+ 'componentWillMount has been renamed, and is not recommended for use. ' +
+ 'See https://fb.me/react-async-component-lifecycle-hooks for details.\n\n' +
+ '* Move component logic from componentWillMount into componentDidMount (preferred in most cases) ' +
+ 'or the constructor.\n' +
+ '* Rename componentWillMount in your code to UNSAFE_componentWillMount to suppress ' +
+ 'this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work.\n' +
+ '* To rename all deprecated lifecycles to their new names, you can run ' +
+ '`npx react-codemod rename-unsafe-lifecycles /path/to/code`.\n' +
+ '\nPlease update the following components: %s',
+ sortedNames,
+ );
}
- if (
- (typeof instance.componentWillUpdate === 'function' &&
- instance.componentWillUpdate.__suppressDeprecationWarning !== true) ||
- typeof instance.UNSAFE_componentWillUpdate === 'function'
- ) {
- unsafeLifecycles.push('UNSAFE_componentWillUpdate');
+
+ if (componentWillReceivePropsUniqueNames.size > 0) {
+ const sortedNames = setToSortedString(
+ componentWillReceivePropsUniqueNames,
+ );
+
+ lowPriorityWarning(
+ false,
+ 'componentWillReceiveProps has been renamed, and is not recommended for use. ' +
+ 'See https://fb.me/react-async-component-lifecycle-hooks for details.\n\n' +
+ '* Move data fetching logic or side effects from componentWillReceiveProps into componentDidUpdate.\n' +
+ "* If you're updating state whenever props change, refactor your " +
+ 'code to use memoization techniques, or move this logic into ' +
+ 'static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state\n' +
+ '* Rename componentWillReceiveProps in your code to UNSAFE_componentWillReceiveProps to suppress ' +
+ 'this warning in non-strict mode.. In React 17.x, only the UNSAFE_ name will work.\n' +
+ '* To rename all deprecated lifecycles to their new names, you can run ' +
+ '`npx react-codemod rename-unsafe-lifecycles /path/to/code`.\n' +
+ '\nPlease update the following components: %s',
+ sortedNames,
+ );
}
- if (unsafeLifecycles.length > 0) {
- unsafeLifecycles.forEach(lifecycle => {
- ((warningsForRoot: any): LifecycleToComponentsMap)[lifecycle].push(
- fiber,
- );
- });
+ if (componentWillUpdateUniqueNames.size > 0) {
+ const sortedNames = setToSortedString(componentWillUpdateUniqueNames);
+
+ lowPriorityWarning(
+ false,
+ 'componentWillUpdate has been renamed, and is not recommended for use. ' +
+ 'See https://fb.me/react-async-component-lifecycle-hooks for details.\n\n' +
+ '* Move data fetching logic or side effects from componentWillUpdate into componentDidUpdate.\n' +
+ '* Rename componentWillUpdate in your code to UNSAFE_componentWillUpdate to suppress ' +
+ 'this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work.\n' +
+ '* To rename all deprecated lifecycles to their new names, you can run ' +
+ '`npx react-codemod rename-unsafe-lifecycles /path/to/code`.\n' +
+ '\nPlease update the following components: %s',
+ sortedNames,
+ );
}
};
+ let pendingLegacyContextWarning: FiberToFiberComponentsMap = new Map();
+
+ // Tracks components we have already warned about.
+ const didWarnAboutLegacyContext = new Set();
+
ReactStrictModeWarnings.recordLegacyContextWarning = (
fiber: Fiber,
instance: any,
@@ -358,6 +360,16 @@ if (__DEV__) {
},
);
};
+
+ ReactStrictModeWarnings.discardPendingWarnings = () => {
+ pendingComponentWillMountWarnings = [];
+ pendingUNSAFE_ComponentWillMountWarnings = [];
+ pendingComponentWillReceivePropsWarnings = [];
+ pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
+ pendingComponentWillUpdateWarnings = [];
+ pendingUNSAFE_ComponentWillUpdateWarnings = [];
+ pendingLegacyContextWarning = new Map();
+ };
}
export default ReactStrictModeWarnings;
diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
index 6557eb0b1dc88..3671f0e759f44 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
@@ -2446,8 +2446,7 @@ describe('ReactIncremental', () => {
ReactNoop.render();
expect(() => expect(Scheduler).toFlushWithoutYielding()).toWarnDev(
[
- 'componentWillReceiveProps: Please update the following components ' +
- 'to use static getDerivedStateFromProps instead: MyComponent',
+ 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
'Legacy context API has been detected within a strict-mode tree: \n\n' +
'Please update the following components: MyComponent',
],
@@ -2887,8 +2886,7 @@ describe('ReactIncremental', () => {
expect(Scheduler).toFlushAndYield([]);
});
- // We don't currently use fibers as keys. Re-enable this test if we
- // ever do again.
+ // We sometimes use Maps with Fibers as keys.
it('does not break with a bad Map polyfill', () => {
const realMapSet = Map.prototype.set;
@@ -2896,17 +2894,27 @@ describe('ReactIncremental', () => {
function Thing() {
throw new Error('No.');
}
+ // This class uses legacy context, which triggers warnings,
+ // the procedures for which use a Map to store fibers.
class Boundary extends React.Component {
state = {didError: false};
componentDidCatch() {
this.setState({didError: true});
}
+ static contextTypes = {
+ color: () => null,
+ };
render() {
return this.state.didError ? null : ;
}
}
ReactNoop.render();
- expect(Scheduler).toFlushWithoutYielding();
+ expect(() => {
+ expect(Scheduler).toFlushWithoutYielding();
+ }).toWarnDev(
+ ['Legacy context API has been detected within a strict-mode tree'],
+ {withoutStack: true},
+ );
}
// First, verify that this code path normally receives Fibers as keys,
@@ -2952,8 +2960,11 @@ describe('ReactIncremental', () => {
};
React = require('react');
ReactNoop = require('react-noop-renderer');
+ Scheduler = require('scheduler');
try {
triggerCodePathThatUsesFibersAsMapKeys();
+ } catch (e) {
+ throw e;
} finally {
// eslint-disable-next-line no-extend-native
Map.prototype.set = realMapSet;
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalPerf-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalPerf-test.internal.js
index fb01a550a114a..b6b957647e044 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalPerf-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalPerf-test.internal.js
@@ -316,10 +316,8 @@ describe('ReactDebugFiberPerf', () => {
addComment('Should not print a warning');
expect(() => expect(Scheduler).toFlushWithoutYielding()).toWarnDev(
[
- 'componentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: NotCascading' +
- '\n\ncomponentWillReceiveProps: Please update the following components ' +
- 'to use static getDerivedStateFromProps instead: NotCascading',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
+ 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
],
{withoutStack: true},
);
@@ -358,14 +356,10 @@ describe('ReactDebugFiberPerf', () => {
addComment('Mount');
expect(() => expect(Scheduler).toFlushWithoutYielding()).toWarnDev(
[
- 'componentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: AllLifecycles' +
- '\n\ncomponentWillReceiveProps: Please update the following components ' +
- 'to use static getDerivedStateFromProps instead: AllLifecycles' +
- '\n\ncomponentWillUpdate: Please update the following components ' +
- 'to use componentDidUpdate instead: AllLifecycles',
- 'Legacy context API has been detected within a strict-mode tree: \n\n' +
- 'Please update the following components: AllLifecycles',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
+ 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
+ 'Using UNSAFE_componentWillUpdate in strict mode is not recommended',
+ 'Legacy context API has been detected within a strict-mode tree',
],
{withoutStack: true},
);
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.internal.js
index e161942a2986a..cf57910c7a530 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalReflection-test.internal.js
@@ -76,8 +76,7 @@ describe('ReactIncrementalReflection', () => {
expect(() =>
expect(Scheduler).toFlushAndYield(['componentDidMount: true']),
).toWarnDev(
- 'componentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: Component',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
{withoutStack: true},
);
@@ -116,8 +115,7 @@ describe('ReactIncrementalReflection', () => {
ReactNoop.render();
expect(() => expect(Scheduler).toFlushAndYield(['Component'])).toWarnDev(
- 'componentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: Component',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
{withoutStack: true},
);
@@ -222,10 +220,10 @@ describe('ReactIncrementalReflection', () => {
expect(() =>
expect(Scheduler).toFlushAndYield([['componentDidMount', span()]]),
).toWarnDev(
- 'componentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: Component' +
- '\n\ncomponentWillUpdate: Please update the following components ' +
- 'to use componentDidUpdate instead: Component',
+ [
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
+ 'Using UNSAFE_componentWillUpdate in strict mode is not recommended',
+ ],
{withoutStack: true},
);
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js
index 58dac988901aa..ccb71f60dfbe2 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.internal.js
@@ -346,8 +346,7 @@ describe('ReactIncrementalUpdates', () => {
}
ReactNoop.render();
expect(() => expect(Scheduler).toFlushWithoutYielding()).toWarnDev(
- 'componentWillReceiveProps: Please update the following components ' +
- 'to use static getDerivedStateFromProps instead: Foo',
+ 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
{withoutStack: true},
);
diff --git a/packages/react/src/__tests__/ReactStrictMode-test.internal.js b/packages/react/src/__tests__/ReactStrictMode-test.internal.js
index d64781dc6bcca..f1401069ccb3e 100644
--- a/packages/react/src/__tests__/ReactStrictMode-test.internal.js
+++ b/packages/react/src/__tests__/ReactStrictMode-test.internal.js
@@ -349,69 +349,10 @@ describe('ReactStrictMode', () => {
});
root.update();
expect(() => Scheduler.unstable_flushAll()).toWarnDev(
- 'Unsafe lifecycle methods were found within a strict-mode tree:' +
- '\n\ncomponentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: AsyncRoot' +
- '\n\ncomponentWillReceiveProps: Please update the following components ' +
- 'to use static getDerivedStateFromProps instead: Bar, Foo' +
- '\n\ncomponentWillUpdate: Please update the following components ' +
- 'to use componentDidUpdate instead: AsyncRoot' +
- '\n\nLearn more about this warning here:' +
- '\nhttps://fb.me/react-strict-mode-warnings',
- {withoutStack: true},
- );
-
- // Dedupe
- root.update();
- Scheduler.unstable_flushAll();
- });
-
- it('should coalesce warnings by lifecycle name', () => {
- class AsyncRoot extends React.Component {
- UNSAFE_componentWillMount() {}
- UNSAFE_componentWillUpdate() {}
- render() {
- return ;
- }
- }
- class Parent extends React.Component {
- componentWillMount() {}
- componentWillUpdate() {}
- componentWillReceiveProps() {}
- render() {
- return ;
- }
- }
- class Child extends React.Component {
- UNSAFE_componentWillReceiveProps() {}
- render() {
- return null;
- }
- }
-
- const root = ReactTestRenderer.create(null, {
- unstable_isConcurrent: true,
- });
- root.update();
-
- expect(() => {
- expect(() => Scheduler.unstable_flushAll()).toWarnDev(
- 'Unsafe lifecycle methods were found within a strict-mode tree:' +
- '\n\ncomponentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: AsyncRoot, Parent' +
- '\n\ncomponentWillReceiveProps: Please update the following components ' +
- 'to use static getDerivedStateFromProps instead: Child, Parent' +
- '\n\ncomponentWillUpdate: Please update the following components ' +
- 'to use componentDidUpdate instead: AsyncRoot, Parent' +
- '\n\nLearn more about this warning here:' +
- '\nhttps://fb.me/react-strict-mode-warnings',
- {withoutStack: true},
- );
- }).toLowPriorityWarnDev(
[
- 'componentWillMount is deprecated',
- 'componentWillReceiveProps is deprecated',
- 'componentWillUpdate is deprecated',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
+ 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
+ 'Using UNSAFE_componentWillUpdate in strict mode is not recommended',
],
{withoutStack: true},
);
@@ -445,21 +386,13 @@ describe('ReactStrictMode', () => {
});
root.update();
expect(() => Scheduler.unstable_flushAll()).toWarnDev(
- 'Unsafe lifecycle methods were found within a strict-mode tree:' +
- '\n\ncomponentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: Foo' +
- '\n\nLearn more about this warning here:' +
- '\nhttps://fb.me/react-strict-mode-warnings',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
{withoutStack: true},
);
root.update();
expect(() => Scheduler.unstable_flushAll()).toWarnDev(
- 'Unsafe lifecycle methods were found within a strict-mode tree:' +
- '\n\ncomponentWillMount: Please update the following components ' +
- 'to use componentDidMount instead: Bar' +
- '\n\nLearn more about this warning here:' +
- '\nhttps://fb.me/react-strict-mode-warnings',
+ 'Using UNSAFE_componentWillMount in strict mode is not recommended',
{withoutStack: true},
);
@@ -507,13 +440,8 @@ describe('ReactStrictMode', () => {
}
expect(() => ReactTestRenderer.create()).toWarnDev(
- 'Unsafe lifecycle methods were found within a strict-mode tree:' +
- '\n in StrictMode (at **)' +
- '\n in SyncRoot (at **)' +
- '\n\ncomponentWillReceiveProps: Please update the following components ' +
- 'to use static getDerivedStateFromProps instead: Bar, Foo' +
- '\n\nLearn more about this warning here:' +
- '\nhttps://fb.me/react-strict-mode-warnings',
+ 'Using UNSAFE_componentWillReceiveProps in strict mode is not recommended',
+ {withoutStack: true},
);
// Dedupe
diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.internal.js b/packages/react/src/__tests__/createReactClassIntegration-test.internal.js
index b1bd7c9b1c913..f6b7a955d5134 100644
--- a/packages/react/src/__tests__/createReactClassIntegration-test.internal.js
+++ b/packages/react/src/__tests__/createReactClassIntegration-test.internal.js
@@ -51,10 +51,7 @@ describe('create-react-class-integration', () => {
});
expect(() => ReactNative.render(, 1)).toLowPriorityWarnDev(
- '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: MyNativeComponent',
+ 'componentWillMount has been renamed',
{withoutStack: true},
);
});
@@ -68,9 +65,7 @@ describe('create-react-class-integration', () => {
});
expect(() => ReactNative.render(, 1)).toLowPriorityWarnDev(
- 'componentWillReceiveProps is deprecated and will be removed in the next major version. ' +
- 'Use static getDerivedStateFromProps instead.' +
- '\n\nPlease update the following components: MyNativeComponent',
+ 'componentWillReceiveProps has been renamed',
{withoutStack: true},
);
});
diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.js b/packages/react/src/__tests__/createReactClassIntegration-test.js
index 36b6bb047732b..542a35087033f 100644
--- a/packages/react/src/__tests__/createReactClassIntegration-test.js
+++ b/packages/react/src/__tests__/createReactClassIntegration-test.js
@@ -560,9 +560,9 @@ describe('create-react-class-integration', () => {
);
}).toLowPriorityWarnDev(
[
- 'componentWillMount is deprecated',
- 'componentWillReceiveProps is deprecated',
- 'componentWillUpdate is deprecated',
+ 'componentWillMount has been renamed',
+ 'componentWillReceiveProps has been renamed',
+ 'componentWillUpdate has been renamed',
],
{withoutStack: true},
);
@@ -604,9 +604,9 @@ describe('create-react-class-integration', () => {
);
}).toLowPriorityWarnDev(
[
- 'componentWillMount is deprecated',
- 'componentWillReceiveProps is deprecated',
- 'componentWillUpdate is deprecated',
+ 'componentWillMount has been renamed',
+ 'componentWillReceiveProps has been renamed',
+ 'componentWillUpdate has been renamed',
],
{withoutStack: true},
);
@@ -649,9 +649,9 @@ describe('create-react-class-integration', () => {
ReactDOM.render(, div),
).toLowPriorityWarnDev(
[
- 'componentWillMount is deprecated',
- 'componentWillReceiveProps is deprecated',
- 'componentWillUpdate is deprecated',
+ 'componentWillMount has been renamed',
+ 'componentWillReceiveProps has been renamed',
+ 'componentWillUpdate has been renamed',
],
{withoutStack: true},
);
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index 1e1ec36101841..f71eb8275ed9b 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -15,7 +15,7 @@ import typeof * as PersistentFeatureFlagsType from './ReactFeatureFlags.persiste
export const debugRenderPhaseSideEffects = false;
export const debugRenderPhaseSideEffectsForStrictMode = false;
export const enableUserTimingAPI = __DEV__;
-export const warnAboutDeprecatedLifecycles = false;
+export const warnAboutDeprecatedLifecycles = true;
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
export const enableProfilerTimer = false;
export const enableSchedulerTracing = false;
diff --git a/scripts/print-warnings/print-warnings.js b/scripts/print-warnings/print-warnings.js
index db7630e9787fb..cba05817d3e82 100644
--- a/scripts/print-warnings/print-warnings.js
+++ b/scripts/print-warnings/print-warnings.js
@@ -52,8 +52,17 @@ function transform(file, enc, cb) {
// warning messages can be concatenated (`+`) at runtime, so here's
// a trivial partial evaluator that interprets the literal value
- const warningMsgLiteral = evalToString(node.arguments[1]);
- warnings.add(JSON.stringify(warningMsgLiteral));
+ try {
+ const warningMsgLiteral = evalToString(node.arguments[1]);
+ warnings.add(JSON.stringify(warningMsgLiteral));
+ } catch (error) {
+ console.error(
+ 'Failed to extract warning message from',
+ file.path
+ );
+ console.error(astPath.node.loc);
+ throw error;
+ }
}
},
},