diff --git a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js
index a1633fa1e625c..7c701219ec3de 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js
@@ -13,10 +13,12 @@ let React;
let ReactDOM;
let PropTypes;
let ReactDOMClient;
-let root;
let Scheduler;
+
let act;
+let assertConsoleErrorDev;
let assertLog;
+let root;
describe('ReactDOMFiber', () => {
let container;
@@ -29,7 +31,7 @@ describe('ReactDOMFiber', () => {
ReactDOMClient = require('react-dom/client');
Scheduler = require('scheduler');
act = require('internal-test-utils').act;
- assertLog = require('internal-test-utils').assertLog;
+ ({assertConsoleErrorDev, assertLog} = require('internal-test-utils'));
container = document.createElement('div');
document.body.appendChild(container);
@@ -732,6 +734,10 @@ describe('ReactDOMFiber', () => {
await act(async () => {
root.render();
});
+ assertConsoleErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(container.innerHTML).toBe('');
expect(portalContainer.innerHTML).toBe('
Loading:
A
diff --git a/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js b/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js
index b81dea9f18263..c799e3b904b86 100644
--- a/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js
@@ -786,7 +786,12 @@ describe('ReactDOMLegacyFiber', () => {
}
}
- ReactDOM.render(
, container);
+ expect(() => {
+ ReactDOM.render(
, container);
+ }).toErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(container.innerHTML).toBe('');
expect(portalContainer.innerHTML).toBe('
bar
');
});
@@ -829,7 +834,13 @@ describe('ReactDOMLegacyFiber', () => {
}
}
- const instance = ReactDOM.render(
, container);
+ let instance;
+ expect(() => {
+ instance = ReactDOM.render(
, container);
+ }).toErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(portalContainer.innerHTML).toBe('
initial-initial
');
expect(container.innerHTML).toBe('');
instance.setState({bar: 'changed'});
@@ -871,7 +882,12 @@ describe('ReactDOMLegacyFiber', () => {
}
}
- ReactDOM.render(
, container);
+ expect(() => {
+ ReactDOM.render(
, container);
+ }).toErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(portalContainer.innerHTML).toBe('
initial-initial
');
expect(container.innerHTML).toBe('');
ReactDOM.render(
, container);
diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationLegacyContext-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationLegacyContext-test.js
index c28bf2d8e4e2c..130bd2310e24a 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationLegacyContext-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationLegacyContext-test.js
@@ -80,6 +80,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 2,
);
expect(e.textContent).toBe('purple');
});
@@ -94,6 +95,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 2,
);
expect(e.textContent).toBe('purple');
});
@@ -110,6 +112,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 1,
);
expect(e.textContent).toBe('');
});
@@ -124,6 +127,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 1,
);
expect(e.textContent).toBe('');
});
@@ -141,6 +145,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 2,
);
expect(e.textContent).toBe('');
});
@@ -158,6 +163,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 2,
);
expect(e.textContent).toBe('');
});
@@ -174,6 +180,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 2,
);
expect(e.textContent).toBe('purple');
});
@@ -190,6 +197,7 @@ describe('ReactDOMServerIntegration', () => {
,
+ 2,
);
expect(e.textContent).toBe('red');
});
@@ -228,7 +236,7 @@ describe('ReactDOMServerIntegration', () => {
text2: PropTypes.string,
};
- const e = await render(
);
+ const e = await render(
, 3);
expect(e.querySelector('#first').textContent).toBe('purple');
expect(e.querySelector('#second').textContent).toBe('red');
});
@@ -254,7 +262,7 @@ describe('ReactDOMServerIntegration', () => {
};
Child.contextTypes = {text: PropTypes.string};
- const e = await render(
);
+ const e = await render(
, 2);
expect(e.textContent).toBe('foo');
},
);
@@ -278,7 +286,8 @@ describe('ReactDOMServerIntegration', () => {
}
const e = await render(
,
- render === clientRenderOnBadMarkup ? 2 : 1,
+ // Some warning is not de-duped and logged again on the client retry render.
+ render === clientRenderOnBadMarkup ? 3 : 2,
);
expect(e.textContent).toBe('nope');
},
diff --git a/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js b/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
index f45069e91539c..2d7696eee5c58 100644
--- a/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
@@ -36,6 +36,7 @@ describe('ReactErrorBoundaries', () => {
let RetryErrorBoundary;
let Normal;
let assertLog;
+ let assertConsoleErrorDev;
beforeEach(() => {
jest.useFakeTimers();
@@ -47,8 +48,7 @@ describe('ReactErrorBoundaries', () => {
act = require('internal-test-utils').act;
Scheduler = require('scheduler');
- const InternalTestUtils = require('internal-test-utils');
- assertLog = InternalTestUtils.assertLog;
+ ({assertLog, assertConsoleErrorDev} = require('internal-test-utils'));
BrokenConstructor = class extends React.Component {
constructor(props) {
@@ -895,6 +895,9 @@ describe('ReactErrorBoundaries', () => {
,
);
});
+ assertConsoleErrorDev([
+ 'BrokenComponentWillMountWithContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ ]);
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
});
diff --git a/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js b/packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
index d3b8926ecd3ea..d2b1aaa4346d0 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,11 @@ describe('ReactFunctionComponent', () => {
root.render(
);
});
+ assertConsoleErrorDev([
+ 'GrandParent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ '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 +478,10 @@ describe('ReactFunctionComponent', () => {
await act(() => {
root.render(
);
});
+ assertConsoleErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ '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-dom/src/__tests__/ReactLegacyCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js
index eaf49b7527e38..cb8b07e72bdcf 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js
@@ -14,7 +14,9 @@ let ReactDOM;
let findDOMNode;
let ReactDOMClient;
let PropTypes;
+
let act;
+let assertConsoleErrorDev;
describe('ReactLegacyCompositeComponent', () => {
beforeEach(() => {
@@ -26,7 +28,7 @@ describe('ReactLegacyCompositeComponent', () => {
ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
.findDOMNode;
PropTypes = require('prop-types');
- act = require('internal-test-utils').act;
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
});
// @gate !disableLegacyMode
@@ -119,6 +121,10 @@ describe('ReactLegacyCompositeComponent', () => {
await act(() => {
root.render(
(component = current)} />);
});
+ assertConsoleErrorDev([
+ 'Child uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Grandchild uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(findDOMNode(component).innerHTML).toBe('bar');
});
@@ -183,6 +189,11 @@ describe('ReactLegacyCompositeComponent', () => {
expect(parentInstance.state.flag).toBe(false);
expect(childInstance.context).toEqual({foo: 'bar', flag: false});
+ assertConsoleErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
+
await act(() => {
parentInstance.setState({flag: true});
});
@@ -242,6 +253,11 @@ describe('ReactLegacyCompositeComponent', () => {
root.render( (wrapper = current)} />);
});
+ assertConsoleErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
+
expect(wrapper.parentRef.current.state.flag).toEqual(true);
expect(wrapper.childRef.current.context).toEqual({flag: true});
@@ -317,6 +333,13 @@ describe('ReactLegacyCompositeComponent', () => {
root.render();
});
+ assertConsoleErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ 'Grandchild uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
+
expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
expect(grandchildInstance.context).toEqual({foo: 'bar', depth: 1});
});
@@ -369,6 +392,9 @@ describe('ReactLegacyCompositeComponent', () => {
await act(() => {
root.render( (parentInstance = current)} />);
});
+ assertConsoleErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ ]);
expect(childInstance).toBeNull();
@@ -376,6 +402,10 @@ describe('ReactLegacyCompositeComponent', () => {
await act(() => {
parentInstance.setState({flag: true});
});
+ assertConsoleErrorDev([
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
+
expect(parentInstance.state.flag).toBe(true);
expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
@@ -435,7 +465,12 @@ describe('ReactLegacyCompositeComponent', () => {
}
const div = document.createElement('div');
- ReactDOM.render(, div);
+ expect(() => {
+ ReactDOM.render(, div);
+ }).toErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Leaf uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(div.children[0].innerHTML).toBe('noise');
div.children[0].innerHTML = 'aliens';
div.children[0].id = 'aliens';
@@ -537,20 +572,26 @@ describe('ReactLegacyCompositeComponent', () => {
const div = document.createElement('div');
let parentInstance = null;
- ReactDOM.render(
- (parentInstance = inst)}>
-
- A1
- A2
-
-
-
- B1
- B2
-
- ,
- div,
- );
+ expect(() => {
+ ReactDOM.render(
+ (parentInstance = inst)}>
+
+ A1
+ A2
+
+
+
+ B1
+ B2
+
+ ,
+ div,
+ );
+ }).toErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'GrandChild uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ 'ChildWithContext uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
parentInstance.setState({
foo: 'def',
@@ -733,12 +774,17 @@ describe('ReactLegacyCompositeComponent', () => {
}
const div = document.createElement('div');
- ReactDOM.render(
-
-
- ,
- div,
- );
+ expect(() => {
+ ReactDOM.render(
+
+
+ ,
+ div,
+ );
+ }).toErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
});
it('should replace state in legacy mode', async () => {
diff --git a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
index f45fae7bbd7e1..2444900ec6fef 100644
--- a/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
@@ -849,6 +849,9 @@ describe('ReactLegacyErrorBoundaries', () => {
,
container,
);
+ assertConsoleErrorDev([
+ 'BrokenComponentWillMountWithContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ ]);
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
});
diff --git a/packages/react-dom/src/__tests__/ReactServerRendering-test.js b/packages/react-dom/src/__tests__/ReactServerRendering-test.js
index 065f7cadd7f85..71e6ce7224be5 100644
--- a/packages/react-dom/src/__tests__/ReactServerRendering-test.js
+++ b/packages/react-dom/src/__tests__/ReactServerRendering-test.js
@@ -371,11 +371,17 @@ describe('ReactDOMServer', () => {
text: PropTypes.string,
};
- const markup = ReactDOMServer.renderToStaticMarkup(
-
-
- ,
- );
+ let markup;
+ expect(() => {
+ markup = ReactDOMServer.renderToStaticMarkup(
+
+
+ ,
+ );
+ }).toErrorDev([
+ 'ContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(markup).toContain('hello, world');
});
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..d53ee83777cb0 100644
--- a/packages/react-reconciler/src/ReactFiberBeginWork.js
+++ b/packages/react-reconciler/src/ReactFiberBeginWork.js
@@ -316,6 +316,7 @@ let didReceiveUpdate: boolean = false;
let didWarnAboutBadClass;
let didWarnAboutContextTypeOnFunctionComponent;
+let didWarnAboutContextTypes;
let didWarnAboutGetDerivedStateOnFunctionComponent;
let didWarnAboutFunctionRefs;
export let didWarnAboutReassigningProps: boolean;
@@ -326,6 +327,7 @@ let didWarnAboutDefaultPropsOnFunctionComponent;
if (__DEV__) {
didWarnAboutBadClass = ({}: {[string]: boolean});
didWarnAboutContextTypeOnFunctionComponent = ({}: {[string]: boolean});
+ didWarnAboutContextTypes = ({}: {[string]: boolean});
didWarnAboutGetDerivedStateOnFunctionComponent = ({}: {[string]: boolean});
didWarnAboutFunctionRefs = ({}: {[string]: boolean});
didWarnAboutReassigningProps = false;
@@ -1130,12 +1132,27 @@ 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) {
+ const componentName = getComponentNameFromType(Component) || 'Unknown';
+
+ if (!didWarnAboutContextTypes[componentName]) {
+ didWarnAboutContextTypes[componentName] = true;
+ if (disableLegacyContext) {
+ console.error(
+ '%s uses the legacy contextTypes API which was removed in React 19. ' +
+ 'Use React.createContext() with React.useContext() instead. ' +
+ '(https://react.dev/link/legacy-context)',
+ componentName,
+ );
+ } else {
+ console.error(
+ '%s uses the legacy contextTypes API which will be removed soon. ' +
+ 'Use React.createContext() with React.useContext() instead. ' +
+ '(https://react.dev/link/legacy-context)',
+ componentName,
+ );
+ }
+ }
}
}
}
@@ -1923,14 +1940,12 @@ function mountIncompleteClassComponent(
function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
if (__DEV__) {
- if (Component) {
- if (Component.childContextTypes) {
- console.error(
- 'childContextTypes cannot be defined on a function component.\n' +
- ' %s.childContextTypes = ...',
- Component.displayName || Component.name || 'Component',
- );
- }
+ if (Component && Component.childContextTypes) {
+ console.error(
+ 'childContextTypes cannot be defined on a function component.\n' +
+ ' %s.childContextTypes = ...',
+ Component.displayName || Component.name || 'Component',
+ );
}
if (!enableRefAsProp && workInProgress.ref !== null) {
let info = '';
diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js
index 4f7ef8530a908..f1419d1aad047 100644
--- a/packages/react-reconciler/src/ReactFiberClassComponent.js
+++ b/packages/react-reconciler/src/ReactFiberClassComponent.js
@@ -393,7 +393,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
didWarnAboutChildContextTypes.add(ctor);
console.error(
'%s uses the legacy childContextTypes API which was removed in React 19. ' +
- 'Use React.createContext() instead.',
+ 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
name,
);
}
@@ -401,7 +401,8 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
didWarnAboutContextTypes.add(ctor);
console.error(
'%s uses the legacy contextTypes API which was removed in React 19. ' +
- 'Use React.createContext() with static contextType instead.',
+ 'Use React.createContext() with static contextType instead. ' +
+ '(https://react.dev/link/legacy-context)',
name,
);
}
@@ -426,6 +427,23 @@ 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. (https://react.dev/link/legacy-context)',
+ 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. ' +
+ '(https://react.dev/link/legacy-context)',
+ name,
+ );
+ }
}
if (typeof instance.componentShouldUpdate === 'function') {
diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
index 2ce110bebe55a..399e4c01b98cb 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js
@@ -14,6 +14,8 @@ let React;
let ReactNoop;
let Scheduler;
let PropTypes;
+
+let assertConsoleErrorDev;
let waitForAll;
let waitFor;
let waitForThrow;
@@ -27,11 +29,13 @@ describe('ReactIncremental', () => {
Scheduler = require('scheduler');
PropTypes = require('prop-types');
- const InternalTestUtils = require('internal-test-utils');
- waitForAll = InternalTestUtils.waitForAll;
- waitFor = InternalTestUtils.waitFor;
- waitForThrow = InternalTestUtils.waitForThrow;
- assertLog = InternalTestUtils.assertLog;
+ ({
+ assertConsoleErrorDev,
+ waitForAll,
+ waitFor,
+ waitForThrow,
+ assertLog,
+ } = require('internal-test-utils'));
});
// Note: This is based on a similar component we use in www. We can delete
@@ -1793,6 +1797,11 @@ describe('ReactIncremental', () => {
'ShowLocale {"locale":"fr"}',
'ShowBoth {"locale":"fr"}',
]);
+ assertConsoleErrorDev([
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ShowLocale uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ 'ShowBoth uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
+ ]);
ReactNoop.render(
@@ -1843,6 +1852,10 @@ describe('ReactIncremental', () => {
'ShowBoth {"locale":"en","route":"/about"}',
'ShowBoth {"locale":"en"}',
]);
+ assertConsoleErrorDev([
+ 'Router uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ShowRoute uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
});
// @gate !disableLegacyContext
@@ -1876,6 +1889,10 @@ describe('ReactIncremental', () => {
'Recurse {"n":1}',
'Recurse {"n":0}',
]);
+ assertConsoleErrorDev([
+ 'Recurse uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Recurse uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
});
// @gate enableLegacyHidden && !disableLegacyContext
@@ -1925,6 +1942,10 @@ describe('ReactIncremental', () => {
'ShowLocale {"locale":"fr"}',
'ShowLocale {"locale":"fr"}',
]);
+ assertConsoleErrorDev([
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ShowLocale uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
await waitForAll([
'ShowLocale {"locale":"fr"}',
@@ -2012,6 +2033,11 @@ describe('ReactIncremental', () => {
'ShowLocaleClass:read {"locale":"fr"}',
'ShowLocaleFn:read {"locale":"fr"}',
]);
+ assertConsoleErrorDev([
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ShowLocaleClass uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ 'ShowLocaleFn uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
+ ]);
statefulInst.setState({x: 1});
await waitForAll([]);
@@ -2098,6 +2124,12 @@ describe('ReactIncremental', () => {
'ShowLocaleFn:read {"locale":"fr"}',
]);
+ assertConsoleErrorDev([
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ShowLocaleClass uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ 'ShowLocaleFn uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
+ ]);
+
statefulInst.setState({locale: 'gr'});
await waitForAll([
// Intl is below setState() so it might have been
@@ -2154,6 +2186,10 @@ describe('ReactIncremental', () => {
ReactNoop.render();
await waitForAll([]);
+ assertConsoleErrorDev([
+ 'Child uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ ]);
+
// Trigger an update in the middle of the tree
instance.setState({});
await waitForAll([]);
@@ -2199,7 +2235,9 @@ describe('ReactIncremental', () => {
// Init
ReactNoop.render();
- await waitForAll([]);
+ await expect(async () => await waitForAll([])).toErrorDev([
+ 'ContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ ]);
// Trigger an update in the middle of the tree
// This is necessary to reproduce the error as it currently exists.
@@ -2252,6 +2290,10 @@ describe('ReactIncremental', () => {
'render',
'componentDidUpdate',
]);
+
+ assertConsoleErrorDev([
+ 'MyComponent uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
});
// eslint-disable-next-line jest/no-disabled-tests
@@ -2384,6 +2426,10 @@ describe('ReactIncremental', () => {
);
await waitForAll(['count:0']);
+ assertConsoleErrorDev([
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
instance.updateCount();
await waitForAll(['count:1']);
});
@@ -2440,6 +2486,11 @@ describe('ReactIncremental', () => {
);
await waitForAll(['count:0']);
+ assertConsoleErrorDev([
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
instance.updateCount();
await waitForAll(['count:1']);
});
@@ -2505,6 +2556,11 @@ describe('ReactIncremental', () => {
);
await waitForAll(['count:0']);
+ assertConsoleErrorDev([
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
instance.updateCount();
await waitForAll([]);
});
@@ -2580,6 +2636,11 @@ describe('ReactIncremental', () => {
);
await waitForAll(['count:0, name:brian']);
+ assertConsoleErrorDev([
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
topInstance.updateCount();
await waitForAll([]);
middleInstance.updateName('not brian');
@@ -2685,6 +2746,7 @@ describe('ReactIncremental', () => {
await expect(async () => {
await waitForAll([]);
}).toErrorDev([
+ 'Boundary uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
'Legacy context API has been detected within a strict-mode tree',
]);
}
diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
index 0cee8150cb15f..91658c562c948 100644
--- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
@@ -1211,7 +1211,14 @@ describe('ReactIncrementalErrorHandling', () => {
,
);
- await waitForAll([]);
+
+ await expect(async () => {
+ await waitForAll([]);
+ }).toErrorDev([
+ 'Provider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Provider uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ 'Connector uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
+ ]);
// If the context stack does not unwind, span will get 'abcde'
expect(ReactNoop).toMatchRenderedOutput();
diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js
index 59d88a9ffa104..b8fbca8339f5b 100644
--- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js
@@ -17,6 +17,7 @@ let gen;
let waitForAll;
let waitFor;
let waitForThrow;
+let assertConsoleErrorDev;
describe('ReactNewContext', () => {
beforeEach(() => {
@@ -28,10 +29,12 @@ describe('ReactNewContext', () => {
Scheduler = require('scheduler');
gen = require('random-seed');
- const InternalTestUtils = require('internal-test-utils');
- waitForAll = InternalTestUtils.waitForAll;
- waitFor = InternalTestUtils.waitFor;
- waitForThrow = InternalTestUtils.waitForThrow;
+ ({
+ waitForAll,
+ waitFor,
+ waitForThrow,
+ assertConsoleErrorDev,
+ } = require('internal-test-utils'));
});
afterEach(() => {
@@ -1032,6 +1035,9 @@ describe('ReactNewContext', () => {
,
);
await waitForAll(['LegacyProvider', 'App', 'Child']);
+ assertConsoleErrorDev([
+ 'LegacyProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ ]);
expect(ReactNoop).toMatchRenderedOutput();
// Update App with same value (should bail out)
diff --git a/packages/react-server/src/ReactFizzClassComponent.js b/packages/react-server/src/ReactFizzClassComponent.js
index 6ef87f100b57d..08b4aaa94c79b 100644
--- a/packages/react-server/src/ReactFizzClassComponent.js
+++ b/packages/react-server/src/ReactFizzClassComponent.js
@@ -370,7 +370,7 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
didWarnAboutChildContextTypes.add(ctor);
console.error(
'%s uses the legacy childContextTypes API which was removed in React 19. ' +
- 'Use React.createContext() instead.',
+ 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
name,
);
}
@@ -378,7 +378,8 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
didWarnAboutContextTypes.add(ctor);
console.error(
'%s uses the legacy contextTypes API which was removed in React 19. ' +
- 'Use React.createContext() with static contextType instead.',
+ 'Use React.createContext() with static contextType instead. ' +
+ '(https://react.dev/link/legacy-context)',
name,
);
}
@@ -386,7 +387,7 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
if (instance.contextTypes) {
console.error(
'contextTypes was defined as an instance property on %s. Use a static ' +
- 'property to define contextTypes instead.',
+ 'property to define contextTypes instead. (https://react.dev/link/legacy-context)',
name,
);
}
@@ -403,6 +404,23 @@ 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. (https://react.dev/link/legacy-context)',
+ 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. ' +
+ '(https://react.dev/link/legacy-context)',
+ name,
+ );
+ }
}
if (typeof instance.componentShouldUpdate === 'function') {
diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js
index afdc9efbb282c..ce255e58a84c5 100644
--- a/packages/react-server/src/ReactFizzServer.js
+++ b/packages/react-server/src/ReactFizzServer.js
@@ -1638,6 +1638,7 @@ function renderClassComponent(
}
const didWarnAboutBadClass: {[string]: boolean} = {};
+const didWarnAboutContextTypes: {[string]: boolean} = {};
const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
let didWarnAboutReassigningProps = false;
@@ -1688,12 +1689,24 @@ function renderFunctionComponent(
const actionStateMatchingIndex = getActionStateMatchingIndex();
if (__DEV__) {
- 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) {
+ const componentName = getComponentNameFromType(Component) || 'Unknown';
+ if (!didWarnAboutContextTypes[componentName]) {
+ didWarnAboutContextTypes[componentName] = true;
+ if (disableLegacyContext) {
+ console.error(
+ '%s uses the legacy contextTypes API which was removed in React 19. ' +
+ 'Use React.createContext() with React.useContext() instead.',
+ componentName,
+ );
+ } else {
+ console.error(
+ '%s uses the legacy contextTypes API which will be removed soon. ' +
+ 'Use React.createContext() with React.useContext() instead.',
+ componentName,
+ );
+ }
+ }
}
}
if (__DEV__) {
@@ -1771,14 +1784,12 @@ function finishFunctionComponent(
function validateFunctionComponentInDev(Component: any): void {
if (__DEV__) {
- if (Component) {
- if (Component.childContextTypes) {
- console.error(
- 'childContextTypes cannot be defined on a function component.\n' +
- ' %s.childContextTypes = ...',
- Component.displayName || Component.name || 'Component',
- );
- }
+ if (Component && Component.childContextTypes) {
+ console.error(
+ 'childContextTypes cannot be defined on a function component.\n' +
+ ' %s.childContextTypes = ...',
+ Component.displayName || Component.name || 'Component',
+ );
}
if (
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__/ReactContextValidator-test.js b/packages/react/src/__tests__/ReactContextValidator-test.js
index cbe9cb2641276..3f24e5bed8d18 100644
--- a/packages/react/src/__tests__/ReactContextValidator-test.js
+++ b/packages/react/src/__tests__/ReactContextValidator-test.js
@@ -66,11 +66,16 @@ describe('ReactContextValidator', () => {
let instance;
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
- await act(() => {
- root.render(
- (instance = current)} />,
- );
- });
+ await expect(async () => {
+ await act(() => {
+ root.render(
+ (instance = current)} />,
+ );
+ });
+ }).toErrorDev([
+ 'ComponentInFooBarContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(instance.childRef.current.context).toEqual({foo: 'abc'});
});
@@ -139,9 +144,14 @@ describe('ReactContextValidator', () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
- await act(() => {
- root.render();
- });
+ await expect(async () => {
+ await act(() => {
+ root.render();
+ });
+ }).toErrorDev([
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(constructorContext).toEqual({foo: 'abc'});
expect(renderContext).toEqual({foo: 'abc'});
@@ -187,11 +197,10 @@ describe('ReactContextValidator', () => {
await act(() => {
root.render();
});
- }).toErrorDev(
- 'ComponentA.childContextTypes is specified but there is no ' +
- 'getChildContext() method on the instance. You can either define ' +
- 'getChildContext() on ComponentA or remove childContextTypes from it.',
- );
+ }).toErrorDev([
+ 'ComponentA uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ComponentA.childContextTypes is specified but there is no getChildContext() method on the instance. You can either define getChildContext() on ComponentA or remove childContextTypes from it.',
+ ]);
// Warnings should be deduped by component type
let container = document.createElement('div');
@@ -206,11 +215,10 @@ describe('ReactContextValidator', () => {
await act(() => {
root.render();
});
- }).toErrorDev(
- 'ComponentB.childContextTypes is specified but there is no ' +
- 'getChildContext() method on the instance. You can either define ' +
- 'getChildContext() on ComponentB or remove childContextTypes from it.',
- );
+ }).toErrorDev([
+ 'ComponentB uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'ComponentB.childContextTypes is specified but there is no getChildContext() method on the instance. You can either define getChildContext() on ComponentB or remove childContextTypes from it.',
+ ]);
});
// TODO (bvaughn) Remove this test and the associated behavior in the future.
@@ -259,9 +267,10 @@ describe('ReactContextValidator', () => {
root.render();
});
}).toErrorDev([
- 'MiddleMissingContext.childContextTypes is specified but there is no ' +
- 'getChildContext() method on the instance. You can either define getChildContext() ' +
- 'on MiddleMissingContext or remove childContextTypes from it.',
+ 'ParentContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'MiddleMissingContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'MiddleMissingContext.childContextTypes is specified but there is no getChildContext() method on the instance. You can either define getChildContext() on MiddleMissingContext or remove childContextTypes from it.',
+ 'ChildContextConsumer uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
]);
expect(childContext.bar).toBeUndefined();
expect(childContext.foo).toBe('FOO');
@@ -435,10 +444,11 @@ describe('ReactContextValidator', () => {
,
);
});
- }).toErrorDev(
- 'ComponentA declares both contextTypes and contextType static properties. ' +
- 'The legacy contextTypes property will be ignored.',
- );
+ }).toErrorDev([
+ 'ParentContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead',
+ 'ComponentA uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ 'ComponentA declares both contextTypes and contextType static properties. The legacy contextTypes property will be ignored.',
+ ]);
// Warnings should be deduped by component type
let container = document.createElement('div');
@@ -461,10 +471,10 @@ describe('ReactContextValidator', () => {
,
);
});
- }).toErrorDev(
- 'ComponentB declares both contextTypes and contextType static properties. ' +
- 'The legacy contextTypes property will be ignored.',
- );
+ }).toErrorDev([
+ 'ComponentB declares both contextTypes and contextType static properties. The legacy contextTypes property will be ignored.',
+ 'ComponentB uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
});
// @gate enableRenderableContext || !__DEV__
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..2ab9c3fa18c9b 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,33 @@ 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. ' +
+ '(https://react.dev/link/legacy-context)' +
+ '\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. (https://react.dev/link/legacy-context)' +
+ '\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. (https://react.dev/link/legacy-context)' +
+ '\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 +1110,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.',
+] );
});
}
diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.js b/packages/react/src/__tests__/createReactClassIntegration-test.js
index 8adb37cad0592..330150506c384 100644
--- a/packages/react/src/__tests__/createReactClassIntegration-test.js
+++ b/packages/react/src/__tests__/createReactClassIntegration-test.js
@@ -10,6 +10,7 @@
'use strict';
let act;
+let assertConsoleErrorDev;
let PropTypes;
let React;
@@ -19,7 +20,7 @@ let createReactClass;
describe('create-react-class-integration', () => {
beforeEach(() => {
jest.resetModules();
- ({act} = require('internal-test-utils'));
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
PropTypes = require('prop-types');
React = require('react');
ReactDOMClient = require('react-dom/client');
@@ -336,6 +337,10 @@ describe('create-react-class-integration', () => {
await act(() => {
root.render();
});
+ assertConsoleErrorDev([
+ 'Component uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
+ ]);
expect(container.firstChild.className).toBe('foo');
});