diff --git a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
index d3b8926ecd3ea..2c1859eac3777 100644
--- a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
@@ -13,6 +13,7 @@ let PropTypes;
let React;
let ReactDOMClient;
let act;
+let assertConsoleErrorDev;
function FunctionComponent(props) {
return
{props.name}
;
@@ -24,7 +25,7 @@ describe('ReactFunctionComponent', () => {
PropTypes = require('prop-types');
React = require('react');
ReactDOMClient = require('react-dom/client');
- act = require('internal-test-utils').act;
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
});
it('should render stateless component', async () => {
@@ -109,6 +110,10 @@ describe('ReactFunctionComponent', () => {
root.render();
});
+ assertConsoleErrorDev([
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
+
expect(el.textContent).toBe('test');
await act(() => {
@@ -472,6 +477,9 @@ describe('ReactFunctionComponent', () => {
await act(() => {
root.render();
});
+ assertConsoleErrorDev([
+ 'Child uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
+ ]);
expect(el.textContent).toBe('en');
});
diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js
index ed0521bedef9f..46b2ad9cf1fc7 100644
--- a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js
+++ b/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js
@@ -227,27 +227,31 @@ test('handles events on text nodes', () => {
}
const log = [];
- ReactNative.render(
-
-
- log.push('string touchend')}
- onTouchEndCapture={() => log.push('string touchend capture')}
- onTouchStart={() => log.push('string touchstart')}
- onTouchStartCapture={() => log.push('string touchstart capture')}>
- Text Content
-
- log.push('number touchend')}
- onTouchEndCapture={() => log.push('number touchend capture')}
- onTouchStart={() => log.push('number touchstart')}
- onTouchStartCapture={() => log.push('number touchstart capture')}>
- {123}
+ expect(() => {
+ ReactNative.render(
+
+
+ log.push('string touchend')}
+ onTouchEndCapture={() => log.push('string touchend capture')}
+ onTouchStart={() => log.push('string touchstart')}
+ onTouchStartCapture={() => log.push('string touchstart capture')}>
+ Text Content
+
+ log.push('number touchend')}
+ onTouchEndCapture={() => log.push('number touchend capture')}
+ onTouchStart={() => log.push('number touchstart')}
+ onTouchStartCapture={() => log.push('number touchstart capture')}>
+ {123}
+
-
- ,
- 1,
- );
+ ,
+ 1,
+ );
+ }).toErrorDev([
+ 'ContextHack uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ ]);
expect(UIManager.createView).toHaveBeenCalledTimes(5);
diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js
index 5c759d9a52cf1..9625d5ae89c83 100644
--- a/packages/react-reconciler/src/ReactFiberBeginWork.js
+++ b/packages/react-reconciler/src/ReactFiberBeginWork.js
@@ -1130,12 +1130,20 @@ function updateFunctionComponent(
// in updateFuntionComponent but only on mount
validateFunctionComponentInDev(workInProgress, workInProgress.type);
- if (disableLegacyContext && Component.contextTypes) {
- console.error(
- '%s uses the legacy contextTypes API which was removed in React 19. ' +
- 'Use React.createContext() with React.useContext() instead.',
- getComponentNameFromType(Component) || 'Unknown',
- );
+ if (Component.contextTypes) {
+ if (disableLegacyContext) {
+ console.error(
+ '%s uses the legacy contextTypes API which was removed in React 19. ' +
+ 'Use React.createContext() with React.useContext() instead.',
+ getComponentNameFromType(Component) || 'Unknown',
+ );
+ } else {
+ console.error(
+ '%s uses the legacy contextTypes API which will be removed soon. ' +
+ 'Use React.createContext() with React.useContext() instead.',
+ getComponentNameFromType(Component) || 'Unknown',
+ );
+ }
}
}
}
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 4f7ef8530a908..4189546a0815a 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -426,6 +426,22 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
name,
);
}
+ if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
+ didWarnAboutChildContextTypes.add(ctor);
+ console.error(
+ '%s uses the legacy childContextTypes API which will soon be removed. ' +
+ 'Use React.createContext() instead.',
+ name,
+ );
+ }
+ if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
+ didWarnAboutContextTypes.add(ctor);
+ console.error(
+ '%s uses the legacy contextTypes API which will soon be removed. ' +
+ 'Use React.createContext() with static contextType instead.',
+ name,
+ );
+ }
}
if (typeof instance.componentShouldUpdate === 'function') {
diff --git a/packages/react-server/src/ReactFizzClassComponent.js b/packages/react-server/src/ReactFizzClassComponent.js
index 6ef87f100b57d..8fa17bbdc41fd 100644
--- a/packages/react-server/src/ReactFizzClassComponent.js
+++ b/packages/react-server/src/ReactFizzClassComponent.js
@@ -403,6 +403,22 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
name,
);
}
+ if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
+ didWarnAboutChildContextTypes.add(ctor);
+ console.error(
+ '%s uses the legacy childContextTypes API which will soon be removed. ' +
+ 'Use React.createContext() instead.',
+ name,
+ );
+ }
+ if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
+ didWarnAboutContextTypes.add(ctor);
+ console.error(
+ '%s uses the legacy contextTypes API which will soon be removed. ' +
+ 'Use React.createContext() with static contextType instead.',
+ name,
+ );
+ }
}
if (typeof instance.componentShouldUpdate === 'function') {
diff --git a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
index 4a4d07a78e592..22ef789f4f6e9 100644
--- a/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
+++ b/packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
@@ -254,7 +254,12 @@ describe 'ReactCoffeeScriptClass', ->
render: ->
React.createElement Foo
- test React.createElement(Outer), 'SPAN', 'foo'
+ expect(->
+ test React.createElement(Outer), 'SPAN', 'foo'
+ ).toErrorDev([
+ 'Outer uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Foo uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ])
it 'renders only once when setting state in componentWillMount', ->
renderCount = 0
@@ -537,7 +542,14 @@ describe 'ReactCoffeeScriptClass', ->
render: ->
React.createElement Bar
- test React.createElement(Foo), 'DIV', 'bar-through-context'
+ expect(->
+ test React.createElement(Foo), 'DIV', 'bar-through-context'
+ ).toErrorDev(
+ [
+ 'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ],
+ )
if !featureFlags.disableStringRefs
it 'supports string refs', ->
diff --git a/packages/react/src/__tests__/ReactES6Class-test.js b/packages/react/src/__tests__/ReactES6Class-test.js
index 769bf5b9a5daf..3ac0b18e8e753 100644
--- a/packages/react/src/__tests__/ReactES6Class-test.js
+++ b/packages/react/src/__tests__/ReactES6Class-test.js
@@ -13,6 +13,7 @@ let PropTypes;
let React;
let ReactDOM;
let ReactDOMClient;
+let assertConsoleErrorDev;
describe('ReactES6Class', () => {
let container;
@@ -30,6 +31,7 @@ describe('ReactES6Class', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
+ ({assertConsoleErrorDev} = require('internal-test-utils'));
container = document.createElement('div');
root = ReactDOMClient.createRoot(container);
attachedListener = null;
@@ -287,6 +289,11 @@ describe('ReactES6Class', () => {
className: PropTypes.string,
};
runTest(, 'SPAN', 'foo');
+
+ assertConsoleErrorDev([
+ 'Outer uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Foo uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
});
}
@@ -579,6 +586,10 @@ describe('ReactES6Class', () => {
}
Foo.childContextTypes = {bar: PropTypes.string};
runTest(, 'DIV', 'bar-through-context');
+ assertConsoleErrorDev([
+ 'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
});
}
diff --git a/packages/react/src/__tests__/ReactStrictMode-test.js b/packages/react/src/__tests__/ReactStrictMode-test.js
index 6a887fc9d9808..0f40118c65caa 100644
--- a/packages/react/src/__tests__/ReactStrictMode-test.js
+++ b/packages/react/src/__tests__/ReactStrictMode-test.js
@@ -18,6 +18,7 @@ let act;
let useMemo;
let useState;
let useReducer;
+let assertConsoleErrorDev;
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
@@ -28,7 +29,7 @@ describe('ReactStrictMode', () => {
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
- act = require('internal-test-utils').act;
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
useMemo = React.useMemo;
useState = React.useState;
useReducer = React.useReducer;
@@ -1072,11 +1073,32 @@ describe('context legacy', () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
- await expect(async () => {
- await act(() => {
- root.render();
- });
- }).toErrorDev(
+ await act(() => {
+ root.render();
+ });
+
+ assertConsoleErrorDev([
+ 'LegacyContextProvider uses the legacy childContextTypes API ' +
+ 'which will soon be removed. Use React.createContext() instead.' +
+ '\n in LegacyContextProvider (at **)' +
+ '\n in div (at **)' +
+ '\n in Root (at **)',
+ 'LegacyContextConsumer uses the legacy contextTypes API which ' +
+ 'will soon be removed. Use React.createContext() with static ' +
+ 'contextType instead.' +
+ '\n in LegacyContextConsumer (at **)' +
+ '\n in div (at **)' +
+ '\n in LegacyContextProvider (at **)' +
+ '\n in div (at **)' +
+ '\n in Root (at **)',
+ 'FunctionalLegacyContextConsumer uses the legacy contextTypes ' +
+ 'API which will be removed soon. Use React.createContext() ' +
+ 'with React.useContext() instead.' +
+ '\n in FunctionalLegacyContextConsumer (at **)' +
+ '\n in div (at **)' +
+ '\n in LegacyContextProvider (at **)' +
+ '\n in div (at **)' +
+ '\n in Root (at **)',
'Legacy context API has been detected within a strict-mode tree.' +
'\n\nThe old API will be supported in all 16.x releases, but applications ' +
'using it should migrate to the new version.' +
@@ -1087,7 +1109,7 @@ describe('context legacy', () => {
'\n in LegacyContextProvider (at **)' +
'\n in div (at **)' +
'\n in Root (at **)',
- );
+ ]);
// Dedupe
await act(() => {
diff --git a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts
index 139a5c01e8d4b..4f4564d356a8c 100644
--- a/packages/react/src/__tests__/ReactTypeScriptClass-test.ts
+++ b/packages/react/src/__tests__/ReactTypeScriptClass-test.ts
@@ -518,7 +518,10 @@ describe('ReactTypeScriptClass', function() {
if (!ReactFeatureFlags.disableLegacyContext) {
it('renders based on context in the constructor', function() {
- test(React.createElement(ProvideChildContextTypes), 'SPAN', 'foo');
+ expect(() => test(React.createElement(ProvideChildContextTypes), 'SPAN', 'foo')).toErrorDev([
+ 'ProvideChildContextTypes uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'StateBasedOnContext uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.'
+ ]);
});
}
@@ -687,8 +690,11 @@ describe('ReactTypeScriptClass', function() {
});
if (!ReactFeatureFlags.disableLegacyContext) {
- it('supports this.context passed via getChildContext', function() {
- test(React.createElement(ProvideContext), 'DIV', 'bar-through-context');
+ it('supports this.context passed via getChildContext', () => {
+ expect(() => test(React.createElement(ProvideContext), 'DIV', 'bar-through-context')).toErrorDev([
+ 'ProvideContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ReadContext uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+] );
});
}