Skip to content

Commit 031dd1c

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 031dd1c

File tree

9 files changed

+142
-39
lines changed

9 files changed

+142
-39
lines changed

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

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ let act;
1818
let useMemo;
1919
let useState;
2020
let useReducer;
21+
let assertConsoleErrorDev;
2122

2223
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
2324

@@ -28,7 +29,7 @@ describe('ReactStrictMode', () => {
2829
ReactDOM = require('react-dom');
2930
ReactDOMClient = require('react-dom/client');
3031
ReactDOMServer = require('react-dom/server');
31-
act = require('internal-test-utils').act;
32+
({act, assertConsoleErrorDev} = require('internal-test-utils'));
3233
useMemo = React.useMemo;
3334
useState = React.useState;
3435
useReducer = React.useReducer;
@@ -1072,11 +1073,32 @@ describe('context legacy', () => {
10721073

10731074
const container = document.createElement('div');
10741075
const root = ReactDOMClient.createRoot(container);
1075-
await expect(async () => {
1076-
await act(() => {
1077-
root.render(<Root />);
1078-
});
1079-
}).toErrorDev(
1076+
await act(() => {
1077+
root.render(<Root />);
1078+
});
1079+
1080+
assertConsoleErrorDev([
1081+
'LegacyContextProvider uses the legacy childContextTypes API ' +
1082+
'which will soon be removed. Use React.createContext() instead.' +
1083+
'\n in LegacyContextProvider (at **)' +
1084+
'\n in div (at **)' +
1085+
'\n in Root (at **)',
1086+
'LegacyContextConsumer uses the legacy contextTypes API which ' +
1087+
'will soon be removed. Use React.createContext() with static ' +
1088+
'contextType instead.' +
1089+
'\n in LegacyContextConsumer (at **)' +
1090+
'\n in div (at **)' +
1091+
'\n in LegacyContextProvider (at **)' +
1092+
'\n in div (at **)' +
1093+
'\n in Root (at **)',
1094+
'FunctionalLegacyContextConsumer uses the legacy contextTypes ' +
1095+
'API which will be removed soon. Use React.createContext() ' +
1096+
'with React.useContext() instead.' +
1097+
'\n in FunctionalLegacyContextConsumer (at **)' +
1098+
'\n in div (at **)' +
1099+
'\n in LegacyContextProvider (at **)' +
1100+
'\n in div (at **)' +
1101+
'\n in Root (at **)',
10801102
'Legacy context API has been detected within a strict-mode tree.' +
10811103
'\n\nThe old API will be supported in all 16.x releases, but applications ' +
10821104
'using it should migrate to the new version.' +
@@ -1087,7 +1109,7 @@ describe('context legacy', () => {
10871109
'\n in LegacyContextProvider (at **)' +
10881110
'\n in div (at **)' +
10891111
'\n in Root (at **)',
1090-
);
1112+
]);
10911113

10921114
// Dedupe
10931115
await act(() => {

packages/react/src/__tests__/ReactTypeScriptClass-test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,10 @@ describe('ReactTypeScriptClass', function() {
518518

519519
if (!ReactFeatureFlags.disableLegacyContext) {
520520
it('renders based on context in the constructor', function() {
521-
test(React.createElement(ProvideChildContextTypes), 'SPAN', 'foo');
521+
expect(() => test(React.createElement(ProvideChildContextTypes), 'SPAN', 'foo')).toErrorDev([
522+
'ProvideChildContextTypes uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
523+
'StateBasedOnContext uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.'
524+
]);
522525
});
523526
}
524527

@@ -687,8 +690,11 @@ describe('ReactTypeScriptClass', function() {
687690
});
688691

689692
if (!ReactFeatureFlags.disableLegacyContext) {
690-
it('supports this.context passed via getChildContext', function() {
691-
test(React.createElement(ProvideContext), 'DIV', 'bar-through-context');
693+
it('supports this.context passed via getChildContext', () => {
694+
expect(() => test(React.createElement(ProvideContext), 'DIV', 'bar-through-context')).toErrorDev([
695+
'ProvideContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
696+
'ReadContext uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
697+
] );
692698
});
693699
}
694700

0 commit comments

Comments
 (0)