Skip to content

Commit 2e7236b

Browse files
committed
Warn about legacy context when legacy context is not disabled
For environments that still have legacy contexts available, this adds a warning to make the remaining call sites easier to locate and encourage upgrades. .
1 parent 553e031 commit 2e7236b

13 files changed

+175
-50
lines changed

packages/react-dom/src/__tests__/ReactDOMFiber-test.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ let React;
1313
let ReactDOM;
1414
let PropTypes;
1515
let ReactDOMClient;
16-
let root;
1716
let Scheduler;
17+
1818
let act;
19+
let assertConsoleErrorDev;
1920
let assertLog;
21+
let root;
2022

2123
describe('ReactDOMFiber', () => {
2224
let container;
@@ -29,7 +31,7 @@ describe('ReactDOMFiber', () => {
2931
ReactDOMClient = require('react-dom/client');
3032
Scheduler = require('scheduler');
3133
act = require('internal-test-utils').act;
32-
assertLog = require('internal-test-utils').assertLog;
34+
({assertConsoleErrorDev, assertLog} = require('internal-test-utils'));
3335

3436
container = document.createElement('div');
3537
document.body.appendChild(container);
@@ -732,6 +734,10 @@ describe('ReactDOMFiber', () => {
732734
await act(async () => {
733735
root.render(<Parent />);
734736
});
737+
assertConsoleErrorDev([
738+
'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
739+
'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
740+
]);
735741
expect(container.innerHTML).toBe('');
736742
expect(portalContainer.innerHTML).toBe('<div>bar</div>');
737743
});

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ let ReactDOMFizzServer;
2727
let ReactDOMFizzStatic;
2828
let Suspense;
2929
let SuspenseList;
30+
31+
let assertConsoleErrorDev;
3032
let useSyncExternalStore;
3133
let useSyncExternalStoreWithSelector;
3234
let use;
@@ -116,12 +118,14 @@ describe('ReactDOMFizzServer', () => {
116118
useActionState = React.useActionState;
117119
}
118120

119-
const InternalTestUtils = require('internal-test-utils');
120-
waitForAll = InternalTestUtils.waitForAll;
121-
waitFor = InternalTestUtils.waitFor;
122-
waitForPaint = InternalTestUtils.waitForPaint;
123-
assertLog = InternalTestUtils.assertLog;
124-
clientAct = InternalTestUtils.act;
121+
({
122+
assertConsoleErrorDev,
123+
assertLog,
124+
act: clientAct,
125+
waitFor,
126+
waitForAll,
127+
waitForPaint,
128+
} = require('internal-test-utils'));
125129

126130
if (gate(flags => flags.source)) {
127131
// The `with-selector` module composes the main `use-sync-external-store`
@@ -1931,6 +1935,10 @@ describe('ReactDOMFizzServer', () => {
19311935
);
19321936
pipe(writable);
19331937
});
1938+
assertConsoleErrorDev([
1939+
'TestProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1940+
'TestConsumer uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
1941+
]);
19341942
expect(getVisibleChildren(container)).toEqual(
19351943
<div>
19361944
Loading: <b>A</b>

packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('ReactErrorBoundaries', () => {
3636
let RetryErrorBoundary;
3737
let Normal;
3838
let assertLog;
39+
let assertConsoleErrorDev;
3940

4041
beforeEach(() => {
4142
jest.useFakeTimers();
@@ -47,8 +48,7 @@ describe('ReactErrorBoundaries', () => {
4748
act = require('internal-test-utils').act;
4849
Scheduler = require('scheduler');
4950

50-
const InternalTestUtils = require('internal-test-utils');
51-
assertLog = InternalTestUtils.assertLog;
51+
({assertLog, assertConsoleErrorDev} = require('internal-test-utils'));
5252

5353
BrokenConstructor = class extends React.Component {
5454
constructor(props) {
@@ -895,6 +895,9 @@ describe('ReactErrorBoundaries', () => {
895895
</ErrorBoundary>,
896896
);
897897
});
898+
assertConsoleErrorDev([
899+
'BrokenComponentWillMountWithContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
900+
]);
898901
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
899902
});
900903

packages/react-dom/src/__tests__/ReactFunctionComponent-test.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ let PropTypes;
1313
let React;
1414
let ReactDOMClient;
1515
let act;
16+
let assertConsoleErrorDev;
1617

1718
function FunctionComponent(props) {
1819
return <div>{props.name}</div>;
@@ -24,7 +25,7 @@ describe('ReactFunctionComponent', () => {
2425
PropTypes = require('prop-types');
2526
React = require('react');
2627
ReactDOMClient = require('react-dom/client');
27-
act = require('internal-test-utils').act;
28+
({act, assertConsoleErrorDev} = require('internal-test-utils'));
2829
});
2930

3031
it('should render stateless component', async () => {
@@ -109,6 +110,10 @@ describe('ReactFunctionComponent', () => {
109110
root.render(<GrandParent test="test" />);
110111
});
111112

113+
assertConsoleErrorDev([
114+
'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
115+
]);
116+
112117
expect(el.textContent).toBe('test');
113118

114119
await act(() => {
@@ -472,6 +477,9 @@ describe('ReactFunctionComponent', () => {
472477
await act(() => {
473478
root.render(<Parent />);
474479
});
480+
assertConsoleErrorDev([
481+
'Child uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
482+
]);
475483
expect(el.textContent).toBe('en');
476484
});
477485

packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -227,27 +227,31 @@ test('handles events on text nodes', () => {
227227
}
228228

229229
const log = [];
230-
ReactNative.render(
231-
<ContextHack>
232-
<Text>
233-
<Text
234-
onTouchEnd={() => log.push('string touchend')}
235-
onTouchEndCapture={() => log.push('string touchend capture')}
236-
onTouchStart={() => log.push('string touchstart')}
237-
onTouchStartCapture={() => log.push('string touchstart capture')}>
238-
Text Content
239-
</Text>
240-
<Text
241-
onTouchEnd={() => log.push('number touchend')}
242-
onTouchEndCapture={() => log.push('number touchend capture')}
243-
onTouchStart={() => log.push('number touchstart')}
244-
onTouchStartCapture={() => log.push('number touchstart capture')}>
245-
{123}
230+
expect(() => {
231+
ReactNative.render(
232+
<ContextHack>
233+
<Text>
234+
<Text
235+
onTouchEnd={() => log.push('string touchend')}
236+
onTouchEndCapture={() => log.push('string touchend capture')}
237+
onTouchStart={() => log.push('string touchstart')}
238+
onTouchStartCapture={() => log.push('string touchstart capture')}>
239+
Text Content
240+
</Text>
241+
<Text
242+
onTouchEnd={() => log.push('number touchend')}
243+
onTouchEndCapture={() => log.push('number touchend capture')}
244+
onTouchStart={() => log.push('number touchstart')}
245+
onTouchStartCapture={() => log.push('number touchstart capture')}>
246+
{123}
247+
</Text>
246248
</Text>
247-
</Text>
248-
</ContextHack>,
249-
1,
250-
);
249+
</ContextHack>,
250+
1,
251+
);
252+
}).toErrorDev([
253+
'ContextHack uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
254+
]);
251255

252256
expect(UIManager.createView).toHaveBeenCalledTimes(5);
253257

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,12 +1130,20 @@ function updateFunctionComponent(
11301130
// in updateFuntionComponent but only on mount
11311131
validateFunctionComponentInDev(workInProgress, workInProgress.type);
11321132

1133-
if (disableLegacyContext && Component.contextTypes) {
1134-
console.error(
1135-
'%s uses the legacy contextTypes API which was removed in React 19. ' +
1136-
'Use React.createContext() with React.useContext() instead.',
1137-
getComponentNameFromType(Component) || 'Unknown',
1138-
);
1133+
if (Component.contextTypes) {
1134+
if (disableLegacyContext) {
1135+
console.error(
1136+
'%s uses the legacy contextTypes API which was removed in React 19. ' +
1137+
'Use React.createContext() with React.useContext() instead.',
1138+
getComponentNameFromType(Component) || 'Unknown',
1139+
);
1140+
} else {
1141+
console.error(
1142+
'%s uses the legacy contextTypes API which will be removed soon. ' +
1143+
'Use React.createContext() with React.useContext() instead.',
1144+
getComponentNameFromType(Component) || 'Unknown',
1145+
);
1146+
}
11391147
}
11401148
}
11411149
}

packages/react-reconciler/src/ReactFiberClassComponent.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,22 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
426426
name,
427427
);
428428
}
429+
if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
430+
didWarnAboutChildContextTypes.add(ctor);
431+
console.error(
432+
'%s uses the legacy childContextTypes API which will soon be removed. ' +
433+
'Use React.createContext() instead.',
434+
name,
435+
);
436+
}
437+
if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
438+
didWarnAboutContextTypes.add(ctor);
439+
console.error(
440+
'%s uses the legacy contextTypes API which will soon be removed. ' +
441+
'Use React.createContext() with static contextType instead.',
442+
name,
443+
);
444+
}
429445
}
430446

431447
if (typeof instance.componentShouldUpdate === 'function') {

packages/react-server/src/ReactFizzClassComponent.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,22 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
403403
name,
404404
);
405405
}
406+
if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
407+
didWarnAboutChildContextTypes.add(ctor);
408+
console.error(
409+
'%s uses the legacy childContextTypes API which will soon be removed. ' +
410+
'Use React.createContext() instead.',
411+
name,
412+
);
413+
}
414+
if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
415+
didWarnAboutContextTypes.add(ctor);
416+
console.error(
417+
'%s uses the legacy contextTypes API which will soon be removed. ' +
418+
'Use React.createContext() with static contextType instead.',
419+
name,
420+
);
421+
}
406422
}
407423

408424
if (typeof instance.componentShouldUpdate === 'function') {

packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,12 @@ describe 'ReactCoffeeScriptClass', ->
254254
render: ->
255255
React.createElement Foo
256256

257-
test React.createElement(Outer), 'SPAN', 'foo'
257+
expect(->
258+
test React.createElement(Outer), 'SPAN', 'foo'
259+
).toErrorDev([
260+
'Outer uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
261+
'Foo uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
262+
])
258263

259264
it 'renders only once when setting state in componentWillMount', ->
260265
renderCount = 0
@@ -537,7 +542,14 @@ describe 'ReactCoffeeScriptClass', ->
537542
render: ->
538543
React.createElement Bar
539544

540-
test React.createElement(Foo), 'DIV', 'bar-through-context'
545+
expect(->
546+
test React.createElement(Foo), 'DIV', 'bar-through-context'
547+
).toErrorDev(
548+
[
549+
'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
550+
'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
551+
],
552+
)
541553

542554
if !featureFlags.disableStringRefs
543555
it 'supports string refs', ->

packages/react/src/__tests__/ReactES6Class-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ let PropTypes;
1313
let React;
1414
let ReactDOM;
1515
let ReactDOMClient;
16+
let assertConsoleErrorDev;
1617

1718
describe('ReactES6Class', () => {
1819
let container;
@@ -30,6 +31,7 @@ describe('ReactES6Class', () => {
3031
React = require('react');
3132
ReactDOM = require('react-dom');
3233
ReactDOMClient = require('react-dom/client');
34+
({assertConsoleErrorDev} = require('internal-test-utils'));
3335
container = document.createElement('div');
3436
root = ReactDOMClient.createRoot(container);
3537
attachedListener = null;
@@ -287,6 +289,11 @@ describe('ReactES6Class', () => {
287289
className: PropTypes.string,
288290
};
289291
runTest(<Outer />, 'SPAN', 'foo');
292+
293+
assertConsoleErrorDev([
294+
'Outer uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
295+
'Foo uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
296+
]);
290297
});
291298
}
292299

@@ -579,6 +586,10 @@ describe('ReactES6Class', () => {
579586
}
580587
Foo.childContextTypes = {bar: PropTypes.string};
581588
runTest(<Foo />, 'DIV', 'bar-through-context');
589+
assertConsoleErrorDev([
590+
'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
591+
'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
592+
]);
582593
});
583594
}
584595

0 commit comments

Comments
 (0)