mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
add legacy context API warning in strict mode (#12849)
* add legacy context APIs warning in strict mode * refactor if statement and the warning message * add other flags for type check * add component stack tree and refactor wording * fix the nits
This commit is contained in:
@@ -668,6 +668,11 @@ function mountClassInstance(
|
||||
workInProgress,
|
||||
instance,
|
||||
);
|
||||
|
||||
ReactStrictModeWarnings.recordLegacyContextWarning(
|
||||
workInProgress,
|
||||
instance,
|
||||
);
|
||||
}
|
||||
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
enableUserTimingAPI,
|
||||
replayFailedUnitOfWorkWithInvokeGuardedCallback,
|
||||
warnAboutDeprecatedLifecycles,
|
||||
warnAboutLegacyContextAPI,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
@@ -440,6 +441,10 @@ function commitAllLifeCycles(
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
ReactStrictModeWarnings.flushPendingDeprecationWarnings();
|
||||
}
|
||||
|
||||
if (warnAboutLegacyContextAPI) {
|
||||
ReactStrictModeWarnings.flushLegacyContextWarning();
|
||||
}
|
||||
}
|
||||
while (nextEffect !== null) {
|
||||
const effectTag = nextEffect.effectTag;
|
||||
|
||||
@@ -21,6 +21,8 @@ type LIFECYCLE =
|
||||
| 'UNSAFE_componentWillUpdate';
|
||||
type LifecycleToComponentsMap = {[lifecycle: LIFECYCLE]: Array<Fiber>};
|
||||
type FiberToLifecycleMap = Map<Fiber, LifecycleToComponentsMap>;
|
||||
type FiberArray = Array<Fiber>;
|
||||
type FiberToFiberComponentsMap = Map<Fiber, FiberArray>;
|
||||
|
||||
const ReactStrictModeWarnings = {
|
||||
discardPendingWarnings(): void {},
|
||||
@@ -28,6 +30,8 @@ const ReactStrictModeWarnings = {
|
||||
flushPendingUnsafeLifecycleWarnings(): void {},
|
||||
recordDeprecationWarnings(fiber: Fiber, instance: any): void {},
|
||||
recordUnsafeLifecycleWarnings(fiber: Fiber, instance: any): void {},
|
||||
recordLegacyContextWarning(fiber: Fiber, instance: any): void {},
|
||||
flushLegacyContextWarning(): void {},
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
@@ -41,10 +45,12 @@ if (__DEV__) {
|
||||
let pendingComponentWillReceivePropsWarnings: Array<Fiber> = [];
|
||||
let pendingComponentWillUpdateWarnings: Array<Fiber> = [];
|
||||
let pendingUnsafeLifecycleWarnings: FiberToLifecycleMap = new Map();
|
||||
let pendingLegacyContextWarning: FiberToFiberComponentsMap = new Map();
|
||||
|
||||
// Tracks components we have already warned about.
|
||||
const didWarnAboutDeprecatedLifecycles = new Set();
|
||||
const didWarnAboutUnsafeLifecycles = new Set();
|
||||
const didWarnAboutLegacyContext = new Set();
|
||||
|
||||
const setToSortedString = set => {
|
||||
const array = [];
|
||||
@@ -59,6 +65,7 @@ if (__DEV__) {
|
||||
pendingComponentWillReceivePropsWarnings = [];
|
||||
pendingComponentWillUpdateWarnings = [];
|
||||
pendingUnsafeLifecycleWarnings = new Map();
|
||||
pendingLegacyContextWarning = new Map();
|
||||
};
|
||||
|
||||
ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = () => {
|
||||
@@ -289,6 +296,67 @@ if (__DEV__) {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ReactStrictModeWarnings.recordLegacyContextWarning = (
|
||||
fiber: Fiber,
|
||||
instance: any,
|
||||
) => {
|
||||
const strictRoot = findStrictRoot(fiber);
|
||||
if (strictRoot === null) {
|
||||
warning(
|
||||
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.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dedup strategy: Warn once per component.
|
||||
if (didWarnAboutLegacyContext.has(fiber.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let warningsForRoot = pendingLegacyContextWarning.get(strictRoot);
|
||||
|
||||
if (
|
||||
typeof instance.getChildContext === 'function' ||
|
||||
fiber.type.contextTypes != null ||
|
||||
fiber.type.childContextTypes != null
|
||||
) {
|
||||
if (warningsForRoot === undefined) {
|
||||
warningsForRoot = [];
|
||||
pendingLegacyContextWarning.set(strictRoot, warningsForRoot);
|
||||
}
|
||||
warningsForRoot.push(fiber);
|
||||
}
|
||||
};
|
||||
|
||||
ReactStrictModeWarnings.flushLegacyContextWarning = () => {
|
||||
((pendingLegacyContextWarning: any): FiberToFiberComponentsMap).forEach(
|
||||
(fiberArray: FiberArray, strictRoot) => {
|
||||
const uniqueNames = new Set();
|
||||
fiberArray.forEach(fiber => {
|
||||
uniqueNames.add(getComponentName(fiber) || 'Component');
|
||||
didWarnAboutLegacyContext.add(fiber.type);
|
||||
});
|
||||
|
||||
const sortedNames = setToSortedString(uniqueNames);
|
||||
const strictRootComponentStack = getStackAddendumByWorkInProgressFiber(
|
||||
strictRoot,
|
||||
);
|
||||
|
||||
warning(
|
||||
false,
|
||||
'Legacy context API has been detected within a strict-mode tree: %s' +
|
||||
'\n\nPlease update the following components: %s' +
|
||||
'\n\nLearn more about this warning here:' +
|
||||
'\nhttps://fb.me/react-strict-mode-warnings',
|
||||
strictRootComponentStack,
|
||||
sortedNames,
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default ReactStrictModeWarnings;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
let ReactTestRenderer;
|
||||
let PropTypes;
|
||||
|
||||
describe('ReactStrictMode', () => {
|
||||
describe('debugRenderPhaseSideEffects', () => {
|
||||
@@ -805,4 +806,72 @@ describe('ReactStrictMode', () => {
|
||||
renderer.update(<OuterComponent />);
|
||||
});
|
||||
});
|
||||
|
||||
describe('context legacy', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
PropTypes = require('prop-types');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutLegacyContextAPI = true;
|
||||
});
|
||||
|
||||
it('should warn if the legacy context API have been used in strict mode', () => {
|
||||
class LegacyContextProvider extends React.Component {
|
||||
getChildContext() {
|
||||
return {color: 'purple'};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <LegacyContextConsumer />;
|
||||
}
|
||||
}
|
||||
|
||||
LegacyContextProvider.childContextTypes = {
|
||||
color: PropTypes.string,
|
||||
};
|
||||
|
||||
class LegacyContextConsumer extends React.Component {
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const {StrictMode} = React;
|
||||
|
||||
class Root extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<StrictMode>
|
||||
<LegacyContextProvider />
|
||||
</StrictMode>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LegacyContextConsumer.contextTypes = {
|
||||
color: PropTypes.string,
|
||||
};
|
||||
|
||||
let rendered;
|
||||
|
||||
expect(() => {
|
||||
rendered = ReactTestRenderer.create(<Root />);
|
||||
}).toWarnDev(
|
||||
'Warning: Legacy context API has been detected within a strict-mode tree: ' +
|
||||
'\n in div (at **)' +
|
||||
'\n in Root (at **)' +
|
||||
'\n\nPlease update the following components: LegacyContextConsumer, LegacyContextProvider' +
|
||||
'\n\nLearn more about this warning here:' +
|
||||
'\nhttps://fb.me/react-strict-mode-warnings',
|
||||
);
|
||||
|
||||
// Dedupe
|
||||
rendered = ReactTestRenderer.create(<Root />);
|
||||
rendered.update(<Root />);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,6 +33,9 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
|
||||
// Warn about legacy context API
|
||||
export const warnAboutLegacyContextAPI = false;
|
||||
|
||||
// Gather advanced timing metrics for Profiler subtrees.
|
||||
export const enableProfilerTimer = __DEV__;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const enableGetDerivedStateFromCatch = false;
|
||||
export const enableSuspense = false;
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
export const warnAboutLegacyContextAPI = __DEV__;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
export const enableProfilerTimer = __DEV__;
|
||||
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
|
||||
|
||||
@@ -18,6 +18,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const enableGetDerivedStateFromCatch = false;
|
||||
export const enableSuspense = false;
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
export const warnAboutLegacyContextAPI = false;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
export const enableProfilerTimer = false;
|
||||
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
|
||||
|
||||
@@ -26,6 +26,7 @@ export const {
|
||||
|
||||
// The rest of the flags are static for better dead code elimination.
|
||||
export const enableUserTimingAPI = __DEV__;
|
||||
export const warnAboutLegacyContextAPI = __DEV__;
|
||||
|
||||
// Only used in www builds.
|
||||
export function addUserTimingListener() {
|
||||
|
||||
@@ -19,6 +19,7 @@ export const enableSuspense = false;
|
||||
export const enableUserTimingAPI = __DEV__;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
export const warnAboutLegacyContextAPI = false;
|
||||
export const enableProfilerTimer = __DEV__;
|
||||
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const enableGetDerivedStateFromCatch = false;
|
||||
export const enableSuspense = false;
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
export const warnAboutLegacyContextAPI = false;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
||||
export const enableProfilerTimer = false;
|
||||
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
|
||||
|
||||
@@ -18,6 +18,7 @@ export const enableUserTimingAPI = __DEV__;
|
||||
export const enableGetDerivedStateFromCatch = false;
|
||||
export const enableSuspense = false;
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
export const warnAboutLegacyContextAPI = false;
|
||||
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
export const enableProfilerTimer = false;
|
||||
export const fireGetDerivedStateFromPropsOnStateUpdates = true;
|
||||
|
||||
@@ -23,6 +23,7 @@ export const {
|
||||
} = require('ReactFeatureFlags');
|
||||
|
||||
// The rest of the flags are static for better dead code elimination.
|
||||
export const warnAboutLegacyContextAPI = __DEV__;
|
||||
|
||||
// In www, we have experimental support for gathering data
|
||||
// from User Timing API calls in production. By default, we
|
||||
|
||||
@@ -14,6 +14,7 @@ const ReactFeatureFlags = {
|
||||
debugRenderPhaseSideEffects: false,
|
||||
debugRenderPhaseSideEffectsForStrictMode: false,
|
||||
warnAboutDeprecatedLifecycles: true,
|
||||
warnAboutLegacyContextAPI: true,
|
||||
};
|
||||
|
||||
module.exports = ReactFeatureFlags;
|
||||
|
||||
Reference in New Issue
Block a user