Warn about legacy context when legacy context is not disabled (#30297)
For environments that still have legacy contexts available, this adds a warning to make the remaining call sites easier to locate and encourage upgrades.
Jan Kassens committed
Jul 10, 2024 at 11:53 UTC
378b305958eb7259cacfce8ad0e66eec07e07074
23 files changed
+465
-150
packages/react-dom/src/__tests__/ReactDOMFiber-test.js
+8
-2
@@ -13,10 +13,12 @@ let React;
13
let ReactDOM;
14
let PropTypes;
15
let ReactDOMClient;
16
-let root;
16
let Scheduler;
17
+
18
let act;
19
+let assertConsoleErrorDev;
20
let assertLog;
21
+let root;
22
23
describe('ReactDOMFiber', () => {
24
let container;
@@ -29,7 +31,7 @@ describe('ReactDOMFiber', () => {
31
ReactDOMClient = require('react-dom/client');
32
Scheduler = require('scheduler');
33
act = require('internal-test-utils').act;
32
- assertLog = require('internal-test-utils').assertLog;
34
+ ({assertConsoleErrorDev, assertLog} = require('internal-test-utils'));
35
36
container = document.createElement('div');
37
document.body.appendChild(container);
@@ -732,6 +734,10 @@ describe('ReactDOMFiber', () => {
734
await act(async () => {
735
root.render(<Parent />);
736
});
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
+ ]);
741
expect(container.innerHTML).toBe('');
742
expect(portalContainer.innerHTML).toBe('<div>bar</div>');
743
});
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+14
-6
@@ -27,6 +27,8 @@ let ReactDOMFizzServer;
27
let ReactDOMFizzStatic;
28
let Suspense;
29
let SuspenseList;
30
+
31
+let assertConsoleErrorDev;
32
let useSyncExternalStore;
33
let useSyncExternalStoreWithSelector;
34
let use;
@@ -116,12 +118,14 @@ describe('ReactDOMFizzServer', () => {
118
useActionState = React.useActionState;
119
}
120
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'));
129
130
if (gate(flags => flags.source)) {
131
// The `with-selector` module composes the main `use-sync-external-store`
@@ -1950,6 +1954,10 @@ describe('ReactDOMFizzServer', () => {
1954
);
1955
pipe(writable);
1956
});
1957
+ assertConsoleErrorDev([
1958
+ 'TestProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1959
+ 'TestConsumer uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
1960
+ ]);
1961
expect(getVisibleChildren(container)).toEqual(
1962
<div>
1963
Loading: <b>A</b>
packages/react-dom/src/__tests__/ReactDOMLegacyFiber-test.js
+19
-3
@@ -786,7 +786,12 @@ describe('ReactDOMLegacyFiber', () => {
786
}
787
}
788
789
- ReactDOM.render(<Parent />, container);
789
+ expect(() => {
790
+ ReactDOM.render(<Parent />, container);
791
+ }).toErrorDev([
792
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
793
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
794
+ ]);
795
expect(container.innerHTML).toBe('');
796
expect(portalContainer.innerHTML).toBe('<div>bar</div>');
797
});
@@ -829,7 +834,13 @@ describe('ReactDOMLegacyFiber', () => {
834
}
835
}
836
832
- const instance = ReactDOM.render(<Parent />, container);
837
+ let instance;
838
+ expect(() => {
839
+ instance = ReactDOM.render(<Parent />, container);
840
+ }).toErrorDev([
841
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
842
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
843
+ ]);
844
expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
845
expect(container.innerHTML).toBe('');
846
instance.setState({bar: 'changed'});
@@ -871,7 +882,12 @@ describe('ReactDOMLegacyFiber', () => {
882
}
883
}
884
874
- ReactDOM.render(<Parent bar="initial" />, container);
885
+ expect(() => {
886
+ ReactDOM.render(<Parent bar="initial" />, container);
887
+ }).toErrorDev([
888
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
889
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
890
+ ]);
891
expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
892
expect(container.innerHTML).toBe('');
893
ReactDOM.render(<Parent bar="changed" />, container);
packages/react-dom/src/__tests__/ReactDOMServerIntegrationLegacyContext-test.js
+12
-3
@@ -80,6 +80,7 @@ describe('ReactDOMServerIntegration', () => {
80
<PurpleContext>
81
<ClassChildWithContext />
82
</PurpleContext>,
83
+ 2,
84
);
85
expect(e.textContent).toBe('purple');
86
});
@@ -94,6 +95,7 @@ describe('ReactDOMServerIntegration', () => {
95
<PurpleContext>
96
<FunctionChildWithContext />
97
</PurpleContext>,
98
+ 2,
99
);
100
expect(e.textContent).toBe('purple');
101
});
@@ -110,6 +112,7 @@ describe('ReactDOMServerIntegration', () => {
112
<PurpleContext>
113
<ClassChildWithoutContext />
114
</PurpleContext>,
115
+ 1,
116
);
117
expect(e.textContent).toBe('');
118
});
@@ -124,6 +127,7 @@ describe('ReactDOMServerIntegration', () => {
127
<PurpleContext>
128
<FunctionChildWithoutContext />
129
</PurpleContext>,
130
+ 1,
131
);
132
expect(e.textContent).toBe('');
133
});
@@ -141,6 +145,7 @@ describe('ReactDOMServerIntegration', () => {
145
<PurpleContext>
146
<ClassChildWithWrongContext />
147
</PurpleContext>,
148
+ 2,
149
);
150
expect(e.textContent).toBe('');
151
});
@@ -158,6 +163,7 @@ describe('ReactDOMServerIntegration', () => {
163
<PurpleContext>
164
<FunctionChildWithWrongContext />
165
</PurpleContext>,
166
+ 2,
167
);
168
expect(e.textContent).toBe('');
169
});
@@ -174,6 +180,7 @@ describe('ReactDOMServerIntegration', () => {
180
<PurpleContext>
181
<Child />
182
</PurpleContext>,
183
+ 2,
184
);
185
expect(e.textContent).toBe('purple');
186
});
@@ -190,6 +197,7 @@ describe('ReactDOMServerIntegration', () => {
197
<Grandchild />
198
</RedContext>
199
</PurpleContext>,
200
+ 2,
201
);
202
expect(e.textContent).toBe('red');
203
});
@@ -228,7 +236,7 @@ describe('ReactDOMServerIntegration', () => {
236
text2: PropTypes.string,
237
};
238
231
- const e = await render(<Parent />);
239
+ const e = await render(<Parent />, 3);
240
expect(e.querySelector('#first').textContent).toBe('purple');
241
expect(e.querySelector('#second').textContent).toBe('red');
242
});
@@ -254,7 +262,7 @@ describe('ReactDOMServerIntegration', () => {
262
};
263
Child.contextTypes = {text: PropTypes.string};
264
257
- const e = await render(<WillMountContext />);
265
+ const e = await render(<WillMountContext />, 2);
266
expect(e.textContent).toBe('foo');
267
},
268
);
@@ -278,7 +286,8 @@ describe('ReactDOMServerIntegration', () => {
286
}
287
const e = await render(
288
<ForgetfulParent />,
281
- render === clientRenderOnBadMarkup ? 2 : 1,
289
+ // Some warning is not de-duped and logged again on the client retry render.
290
+ render === clientRenderOnBadMarkup ? 3 : 2,
291
);
292
expect(e.textContent).toBe('nope');
293
},
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
+5
-2
@@ -36,6 +36,7 @@ describe('ReactErrorBoundaries', () => {
36
let RetryErrorBoundary;
37
let Normal;
38
let assertLog;
39
+ let assertConsoleErrorDev;
40
41
beforeEach(() => {
42
jest.useFakeTimers();
@@ -47,8 +48,7 @@ describe('ReactErrorBoundaries', () => {
48
act = require('internal-test-utils').act;
49
Scheduler = require('scheduler');
50
50
- const InternalTestUtils = require('internal-test-utils');
51
- assertLog = InternalTestUtils.assertLog;
51
+ ({assertLog, assertConsoleErrorDev} = require('internal-test-utils'));
52
53
BrokenConstructor = class extends React.Component {
54
constructor(props) {
@@ -895,6 +895,9 @@ describe('ReactErrorBoundaries', () => {
895
</ErrorBoundary>,
896
);
897
});
898
+ assertConsoleErrorDev([
899
+ 'BrokenComponentWillMountWithContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
900
+ ]);
901
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
902
});
903
packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
+11
-1
@@ -13,6 +13,7 @@ let PropTypes;
13
let React;
14
let ReactDOMClient;
15
let act;
16
+let assertConsoleErrorDev;
17
18
function FunctionComponent(props) {
19
return <div>{props.name}</div>;
@@ -24,7 +25,7 @@ describe('ReactFunctionComponent', () => {
25
PropTypes = require('prop-types');
26
React = require('react');
27
ReactDOMClient = require('react-dom/client');
27
- act = require('internal-test-utils').act;
28
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
29
});
30
31
it('should render stateless component', async () => {
@@ -109,6 +110,11 @@ describe('ReactFunctionComponent', () => {
110
root.render(<GrandParent test="test" />);
111
});
112
113
+ assertConsoleErrorDev([
114
+ 'GrandParent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
115
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
116
+ ]);
117
+
118
expect(el.textContent).toBe('test');
119
120
await act(() => {
@@ -472,6 +478,10 @@ describe('ReactFunctionComponent', () => {
478
await act(() => {
479
root.render(<Parent />);
480
});
481
+ assertConsoleErrorDev([
482
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
483
+ 'Child uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
484
+ ]);
485
expect(el.textContent).toBe('en');
486
});
487
packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js
+68
-22
@@ -14,7 +14,9 @@ let ReactDOM;
14
let findDOMNode;
15
let ReactDOMClient;
16
let PropTypes;
17
+
18
let act;
19
+let assertConsoleErrorDev;
20
21
describe('ReactLegacyCompositeComponent', () => {
22
beforeEach(() => {
@@ -26,7 +28,7 @@ describe('ReactLegacyCompositeComponent', () => {
28
ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
29
.findDOMNode;
30
PropTypes = require('prop-types');
29
- act = require('internal-test-utils').act;
31
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
32
});
33
34
// @gate !disableLegacyMode
@@ -119,6 +121,10 @@ describe('ReactLegacyCompositeComponent', () => {
121
await act(() => {
122
root.render(<Parent ref={current => (component = current)} />);
123
});
124
+ assertConsoleErrorDev([
125
+ 'Child uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
126
+ 'Grandchild uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
127
+ ]);
128
expect(findDOMNode(component).innerHTML).toBe('bar');
129
});
130
@@ -183,6 +189,11 @@ describe('ReactLegacyCompositeComponent', () => {
189
expect(parentInstance.state.flag).toBe(false);
190
expect(childInstance.context).toEqual({foo: 'bar', flag: false});
191
192
+ assertConsoleErrorDev([
193
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
194
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
195
+ ]);
196
+
197
await act(() => {
198
parentInstance.setState({flag: true});
199
});
@@ -242,6 +253,11 @@ describe('ReactLegacyCompositeComponent', () => {
253
root.render(<Wrapper ref={current => (wrapper = current)} />);
254
});
255
256
+ assertConsoleErrorDev([
257
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
258
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
259
+ ]);
260
+
261
expect(wrapper.parentRef.current.state.flag).toEqual(true);
262
expect(wrapper.childRef.current.context).toEqual({flag: true});
263
@@ -317,6 +333,13 @@ describe('ReactLegacyCompositeComponent', () => {
333
root.render(<Parent />);
334
});
335
336
+ assertConsoleErrorDev([
337
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
338
+ 'Child uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
339
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
340
+ 'Grandchild uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
341
+ ]);
342
+
343
expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
344
expect(grandchildInstance.context).toEqual({foo: 'bar', depth: 1});
345
});
@@ -369,6 +392,9 @@ describe('ReactLegacyCompositeComponent', () => {
392
await act(() => {
393
root.render(<Parent ref={current => (parentInstance = current)} />);
394
});
395
+ assertConsoleErrorDev([
396
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
397
+ ]);
398
399
expect(childInstance).toBeNull();
400
@@ -376,6 +402,10 @@ describe('ReactLegacyCompositeComponent', () => {
402
await act(() => {
403
parentInstance.setState({flag: true});
404
});
405
+ assertConsoleErrorDev([
406
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
407
+ ]);
408
+
409
expect(parentInstance.state.flag).toBe(true);
410
411
expect(childInstance.context).toEqual({foo: 'bar', depth: 0});
@@ -435,7 +465,12 @@ describe('ReactLegacyCompositeComponent', () => {
465
}
466
467
const div = document.createElement('div');
438
- ReactDOM.render(<Parent cntxt="noise" />, div);
468
+ expect(() => {
469
+ ReactDOM.render(<Parent cntxt="noise" />, div);
470
+ }).toErrorDev([
471
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
472
+ 'Leaf uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
473
+ ]);
474
expect(div.children[0].innerHTML).toBe('noise');
475
div.children[0].innerHTML = 'aliens';
476
div.children[0].id = 'aliens';
@@ -537,20 +572,26 @@ describe('ReactLegacyCompositeComponent', () => {
572
const div = document.createElement('div');
573
574
let parentInstance = null;
540
- ReactDOM.render(
541
- <Parent ref={inst => (parentInstance = inst)}>
542
- <ChildWithoutContext>
543
- A1
544
- <GrandChild>A2</GrandChild>
545
- </ChildWithoutContext>
546
-
547
- <ChildWithContext>
548
- B1
549
- <GrandChild>B2</GrandChild>
550
- </ChildWithContext>
551
- </Parent>,
552
- div,
553
- );
575
+ expect(() => {
576
+ ReactDOM.render(
577
+ <Parent ref={inst => (parentInstance = inst)}>
578
+ <ChildWithoutContext>
579
+ A1
580
+ <GrandChild>A2</GrandChild>
581
+ </ChildWithoutContext>
582
+
583
+ <ChildWithContext>
584
+ B1
585
+ <GrandChild>B2</GrandChild>
586
+ </ChildWithContext>
587
+ </Parent>,
588
+ div,
589
+ );
590
+ }).toErrorDev([
591
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
592
+ 'GrandChild uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
593
+ 'ChildWithContext uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
594
+ ]);
595
596
parentInstance.setState({
597
foo: 'def',
@@ -733,12 +774,17 @@ describe('ReactLegacyCompositeComponent', () => {
774
}
775
776
const div = document.createElement('div');
736
- ReactDOM.render(
737
- <Parent>
738
- <Component />
739
- </Parent>,
740
- div,
741
- );
777
+ expect(() => {
778
+ ReactDOM.render(
779
+ <Parent>
780
+ <Component />
781
+ </Parent>,
782
+ div,
783
+ );
784
+ }).toErrorDev([
785
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
786
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
787
+ ]);
788
});
789
790
it('should replace state in legacy mode', async () => {
packages/react-dom/src/__tests__/ReactLegacyErrorBoundaries-test.internal.js
+3
@@ -849,6 +849,9 @@ describe('ReactLegacyErrorBoundaries', () => {
849
</ErrorBoundary>,
850
container,
851
);
852
+ assertConsoleErrorDev([
853
+ 'BrokenComponentWillMountWithContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
854
+ ]);
855
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
856
});
857
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+11
-5
@@ -371,11 +371,17 @@ describe('ReactDOMServer', () => {
371
text: PropTypes.string,
372
};
373
374
- const markup = ReactDOMServer.renderToStaticMarkup(
375
- <ContextProvider>
376
- <Component />
377
- </ContextProvider>,
378
- );
374
+ let markup;
375
+ expect(() => {
376
+ markup = ReactDOMServer.renderToStaticMarkup(
377
+ <ContextProvider>
378
+ <Component />
379
+ </ContextProvider>,
380
+ );
381
+ }).toErrorDev([
382
+ 'ContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
383
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
384
+ ]);
385
expect(markup).toContain('hello, world');
386
});
387
packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js
+24
-20
@@ -227,27 +227,31 @@ test('handles events on text nodes', () => {
227
}
228
229
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>
248
</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
+ ]);
255
256
expect(UIManager.createView).toHaveBeenCalledTimes(5);
257
packages/react-reconciler/src/ReactFiberBeginWork.js
+29
-14
@@ -316,6 +316,7 @@ let didReceiveUpdate: boolean = false;
316
317
let didWarnAboutBadClass;
318
let didWarnAboutContextTypeOnFunctionComponent;
319
+let didWarnAboutContextTypes;
320
let didWarnAboutGetDerivedStateOnFunctionComponent;
321
let didWarnAboutFunctionRefs;
322
export let didWarnAboutReassigningProps: boolean;
@@ -326,6 +327,7 @@ let didWarnAboutDefaultPropsOnFunctionComponent;
327
if (__DEV__) {
328
didWarnAboutBadClass = ({}: {[string]: boolean});
329
didWarnAboutContextTypeOnFunctionComponent = ({}: {[string]: boolean});
330
+ didWarnAboutContextTypes = ({}: {[string]: boolean});
331
didWarnAboutGetDerivedStateOnFunctionComponent = ({}: {[string]: boolean});
332
didWarnAboutFunctionRefs = ({}: {[string]: boolean});
333
didWarnAboutReassigningProps = false;
@@ -1130,12 +1132,27 @@ function updateFunctionComponent(
1132
// in updateFuntionComponent but only on mount
1133
validateFunctionComponentInDev(workInProgress, workInProgress.type);
1134
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
- );
1135
+ if (Component.contextTypes) {
1136
+ const componentName = getComponentNameFromType(Component) || 'Unknown';
1137
+
1138
+ if (!didWarnAboutContextTypes[componentName]) {
1139
+ didWarnAboutContextTypes[componentName] = true;
1140
+ if (disableLegacyContext) {
1141
+ console.error(
1142
+ '%s uses the legacy contextTypes API which was removed in React 19. ' +
1143
+ 'Use React.createContext() with React.useContext() instead. ' +
1144
+ '(https://react.dev/link/legacy-context)',
1145
+ componentName,
1146
+ );
1147
+ } else {
1148
+ console.error(
1149
+ '%s uses the legacy contextTypes API which will be removed soon. ' +
1150
+ 'Use React.createContext() with React.useContext() instead. ' +
1151
+ '(https://react.dev/link/legacy-context)',
1152
+ componentName,
1153
+ );
1154
+ }
1155
+ }
1156
}
1157
}
1158
}
@@ -1923,14 +1940,12 @@ function mountIncompleteClassComponent(
1940
1941
function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
1942
if (__DEV__) {
1926
- if (Component) {
1927
- if (Component.childContextTypes) {
1928
- console.error(
1929
- 'childContextTypes cannot be defined on a function component.\n' +
1930
- ' %s.childContextTypes = ...',
1931
- Component.displayName || Component.name || 'Component',
1932
- );
1933
- }
1943
+ if (Component && Component.childContextTypes) {
1944
+ console.error(
1945
+ 'childContextTypes cannot be defined on a function component.\n' +
1946
+ ' %s.childContextTypes = ...',
1947
+ Component.displayName || Component.name || 'Component',
1948
+ );
1949
}
1950
if (!enableRefAsProp && workInProgress.ref !== null) {
1951
let info = '';
packages/react-reconciler/src/ReactFiberClassComponent.js
+20
-2
@@ -393,7 +393,7 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
393
didWarnAboutChildContextTypes.add(ctor);
394
console.error(
395
'%s uses the legacy childContextTypes API which was removed in React 19. ' +
396
- 'Use React.createContext() instead.',
396
+ 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
397
name,
398
);
399
}
@@ -401,7 +401,8 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
401
didWarnAboutContextTypes.add(ctor);
402
console.error(
403
'%s uses the legacy contextTypes API which was removed in React 19. ' +
404
- 'Use React.createContext() with static contextType instead.',
404
+ 'Use React.createContext() with static contextType instead. ' +
405
+ '(https://react.dev/link/legacy-context)',
406
name,
407
);
408
}
@@ -426,6 +427,23 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
427
name,
428
);
429
}
430
+ if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
431
+ didWarnAboutChildContextTypes.add(ctor);
432
+ console.error(
433
+ '%s uses the legacy childContextTypes API which will soon be removed. ' +
434
+ 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
435
+ name,
436
+ );
437
+ }
438
+ if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
439
+ didWarnAboutContextTypes.add(ctor);
440
+ console.error(
441
+ '%s uses the legacy contextTypes API which will soon be removed. ' +
442
+ 'Use React.createContext() with static contextType instead. ' +
443
+ '(https://react.dev/link/legacy-context)',
444
+ name,
445
+ );
446
+ }
447
}
448
449
if (typeof instance.componentShouldUpdate === 'function') {
packages/react-reconciler/src/__tests__/ReactIncremental-test.js
+68
-6
@@ -14,6 +14,8 @@ let React;
14
let ReactNoop;
15
let Scheduler;
16
let PropTypes;
17
+
18
+let assertConsoleErrorDev;
19
let waitForAll;
20
let waitFor;
21
let waitForThrow;
@@ -27,11 +29,13 @@ describe('ReactIncremental', () => {
29
Scheduler = require('scheduler');
30
PropTypes = require('prop-types');
31
30
- const InternalTestUtils = require('internal-test-utils');
31
- waitForAll = InternalTestUtils.waitForAll;
32
- waitFor = InternalTestUtils.waitFor;
33
- waitForThrow = InternalTestUtils.waitForThrow;
34
- assertLog = InternalTestUtils.assertLog;
32
+ ({
33
+ assertConsoleErrorDev,
34
+ waitForAll,
35
+ waitFor,
36
+ waitForThrow,
37
+ assertLog,
38
+ } = require('internal-test-utils'));
39
});
40
41
// Note: This is based on a similar component we use in www. We can delete
@@ -1793,6 +1797,11 @@ describe('ReactIncremental', () => {
1797
'ShowLocale {"locale":"fr"}',
1798
'ShowBoth {"locale":"fr"}',
1799
]);
1800
+ assertConsoleErrorDev([
1801
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1802
+ 'ShowLocale uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
1803
+ 'ShowBoth uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
1804
+ ]);
1805
1806
ReactNoop.render(
1807
<Intl locale="de">
@@ -1843,6 +1852,10 @@ describe('ReactIncremental', () => {
1852
'ShowBoth {"locale":"en","route":"/about"}',
1853
'ShowBoth {"locale":"en"}',
1854
]);
1855
+ assertConsoleErrorDev([
1856
+ 'Router uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1857
+ 'ShowRoute uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
1858
+ ]);
1859
});
1860
1861
// @gate !disableLegacyContext
@@ -1876,6 +1889,10 @@ describe('ReactIncremental', () => {
1889
'Recurse {"n":1}',
1890
'Recurse {"n":0}',
1891
]);
1892
+ assertConsoleErrorDev([
1893
+ 'Recurse uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1894
+ 'Recurse uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
1895
+ ]);
1896
});
1897
1898
// @gate enableLegacyHidden && !disableLegacyContext
@@ -1925,6 +1942,10 @@ describe('ReactIncremental', () => {
1942
'ShowLocale {"locale":"fr"}',
1943
'ShowLocale {"locale":"fr"}',
1944
]);
1945
+ assertConsoleErrorDev([
1946
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1947
+ 'ShowLocale uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
1948
+ ]);
1949
1950
await waitForAll([
1951
'ShowLocale {"locale":"fr"}',
@@ -2012,6 +2033,11 @@ describe('ReactIncremental', () => {
2033
'ShowLocaleClass:read {"locale":"fr"}',
2034
'ShowLocaleFn:read {"locale":"fr"}',
2035
]);
2036
+ assertConsoleErrorDev([
2037
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2038
+ 'ShowLocaleClass uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2039
+ 'ShowLocaleFn uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
2040
+ ]);
2041
2042
statefulInst.setState({x: 1});
2043
await waitForAll([]);
@@ -2098,6 +2124,12 @@ describe('ReactIncremental', () => {
2124
'ShowLocaleFn:read {"locale":"fr"}',
2125
]);
2126
2127
+ assertConsoleErrorDev([
2128
+ 'Intl uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2129
+ 'ShowLocaleClass uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2130
+ 'ShowLocaleFn uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
2131
+ ]);
2132
+
2133
statefulInst.setState({locale: 'gr'});
2134
await waitForAll([
2135
// Intl is below setState() so it might have been
@@ -2154,6 +2186,10 @@ describe('ReactIncremental', () => {
2186
ReactNoop.render(<Root />);
2187
await waitForAll([]);
2188
2189
+ assertConsoleErrorDev([
2190
+ 'Child uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2191
+ ]);
2192
+
2193
// Trigger an update in the middle of the tree
2194
instance.setState({});
2195
await waitForAll([]);
@@ -2199,7 +2235,9 @@ describe('ReactIncremental', () => {
2235
2236
// Init
2237
ReactNoop.render(<Root />);
2202
- await waitForAll([]);
2238
+ await expect(async () => await waitForAll([])).toErrorDev([
2239
+ 'ContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2240
+ ]);
2241
2242
// Trigger an update in the middle of the tree
2243
// This is necessary to reproduce the error as it currently exists.
@@ -2252,6 +2290,10 @@ describe('ReactIncremental', () => {
2290
'render',
2291
'componentDidUpdate',
2292
]);
2293
+
2294
+ assertConsoleErrorDev([
2295
+ 'MyComponent uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2296
+ ]);
2297
});
2298
2299
// eslint-disable-next-line jest/no-disabled-tests
@@ -2384,6 +2426,10 @@ describe('ReactIncremental', () => {
2426
);
2427
2428
await waitForAll(['count:0']);
2429
+ assertConsoleErrorDev([
2430
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2431
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2432
+ ]);
2433
instance.updateCount();
2434
await waitForAll(['count:1']);
2435
});
@@ -2440,6 +2486,11 @@ describe('ReactIncremental', () => {
2486
);
2487
2488
await waitForAll(['count:0']);
2489
+ assertConsoleErrorDev([
2490
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2491
+ 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2492
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2493
+ ]);
2494
instance.updateCount();
2495
await waitForAll(['count:1']);
2496
});
@@ -2505,6 +2556,11 @@ describe('ReactIncremental', () => {
2556
);
2557
2558
await waitForAll(['count:0']);
2559
+ assertConsoleErrorDev([
2560
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2561
+ 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2562
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2563
+ ]);
2564
instance.updateCount();
2565
await waitForAll([]);
2566
});
@@ -2580,6 +2636,11 @@ describe('ReactIncremental', () => {
2636
);
2637
2638
await waitForAll(['count:0, name:brian']);
2639
+ assertConsoleErrorDev([
2640
+ 'TopContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2641
+ 'MiddleContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
2642
+ 'Child uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2643
+ ]);
2644
topInstance.updateCount();
2645
await waitForAll([]);
2646
middleInstance.updateName('not brian');
@@ -2685,6 +2746,7 @@ describe('ReactIncremental', () => {
2746
await expect(async () => {
2747
await waitForAll([]);
2748
}).toErrorDev([
2749
+ 'Boundary uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
2750
'Legacy context API has been detected within a strict-mode tree',
2751
]);
2752
}
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+8
-1
@@ -1211,7 +1211,14 @@ describe('ReactIncrementalErrorHandling', () => {
1211
<Connector />
1212
</Provider>,
1213
);
1214
- await waitForAll([]);
1214
+
1215
+ await expect(async () => {
1216
+ await waitForAll([]);
1217
+ }).toErrorDev([
1218
+ 'Provider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1219
+ 'Provider uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
1220
+ 'Connector uses the legacy contextTypes API which will be removed soon. Use React.createContext() with React.useContext() instead.',
1221
+ ]);
1222
1223
// If the context stack does not unwind, span will get 'abcde'
1224
expect(ReactNoop).toMatchRenderedOutput(<span prop="a" />);
packages/react-reconciler/src/__tests__/ReactNewContext-test.js
+10
-4
@@ -17,6 +17,7 @@ let gen;
17
let waitForAll;
18
let waitFor;
19
let waitForThrow;
20
+let assertConsoleErrorDev;
21
22
describe('ReactNewContext', () => {
23
beforeEach(() => {
@@ -28,10 +29,12 @@ describe('ReactNewContext', () => {
29
Scheduler = require('scheduler');
30
gen = require('random-seed');
31
31
- const InternalTestUtils = require('internal-test-utils');
32
- waitForAll = InternalTestUtils.waitForAll;
33
- waitFor = InternalTestUtils.waitFor;
34
- waitForThrow = InternalTestUtils.waitForThrow;
32
+ ({
33
+ waitForAll,
34
+ waitFor,
35
+ waitForThrow,
36
+ assertConsoleErrorDev,
37
+ } = require('internal-test-utils'));
38
});
39
40
afterEach(() => {
@@ -1032,6 +1035,9 @@ describe('ReactNewContext', () => {
1035
</LegacyProvider>,
1036
);
1037
await waitForAll(['LegacyProvider', 'App', 'Child']);
1038
+ assertConsoleErrorDev([
1039
+ 'LegacyProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
1040
+ ]);
1041
expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
1042
1043
// Update App with same value (should bail out)
packages/react-server/src/ReactFizzClassComponent.js
+21
-3
@@ -370,7 +370,7 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
370
didWarnAboutChildContextTypes.add(ctor);
371
console.error(
372
'%s uses the legacy childContextTypes API which was removed in React 19. ' +
373
- 'Use React.createContext() instead.',
373
+ 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
374
name,
375
);
376
}
@@ -378,7 +378,8 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
378
didWarnAboutContextTypes.add(ctor);
379
console.error(
380
'%s uses the legacy contextTypes API which was removed in React 19. ' +
381
- 'Use React.createContext() with static contextType instead.',
381
+ 'Use React.createContext() with static contextType instead. ' +
382
+ '(https://react.dev/link/legacy-context)',
383
name,
384
);
385
}
@@ -386,7 +387,7 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
387
if (instance.contextTypes) {
388
console.error(
389
'contextTypes was defined as an instance property on %s. Use a static ' +
389
- 'property to define contextTypes instead.',
390
+ 'property to define contextTypes instead. (https://react.dev/link/legacy-context)',
391
name,
392
);
393
}
@@ -403,6 +404,23 @@ function checkClassInstance(instance: any, ctor: any, newProps: any) {
404
name,
405
);
406
}
407
+ if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
408
+ didWarnAboutChildContextTypes.add(ctor);
409
+ console.error(
410
+ '%s uses the legacy childContextTypes API which will soon be removed. ' +
411
+ 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
412
+ name,
413
+ );
414
+ }
415
+ if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
416
+ didWarnAboutContextTypes.add(ctor);
417
+ console.error(
418
+ '%s uses the legacy contextTypes API which will soon be removed. ' +
419
+ 'Use React.createContext() with static contextType instead. ' +
420
+ '(https://react.dev/link/legacy-context)',
421
+ name,
422
+ );
423
+ }
424
}
425
426
if (typeof instance.componentShouldUpdate === 'function') {
packages/react-server/src/ReactFizzServer.js
+25
-14
@@ -1638,6 +1638,7 @@ function renderClassComponent(
1638
}
1639
1640
const didWarnAboutBadClass: {[string]: boolean} = {};
1641
+const didWarnAboutContextTypes: {[string]: boolean} = {};
1642
const didWarnAboutContextTypeOnFunctionComponent: {[string]: boolean} = {};
1643
const didWarnAboutGetDerivedStateOnFunctionComponent: {[string]: boolean} = {};
1644
let didWarnAboutReassigningProps = false;
@@ -1688,12 +1689,24 @@ function renderFunctionComponent(
1689
const actionStateMatchingIndex = getActionStateMatchingIndex();
1690
1691
if (__DEV__) {
1691
- if (disableLegacyContext && Component.contextTypes) {
1692
- console.error(
1693
- '%s uses the legacy contextTypes API which was removed in React 19. ' +
1694
- 'Use React.createContext() with React.useContext() instead.',
1695
- getComponentNameFromType(Component) || 'Unknown',
1696
- );
1692
+ if (Component.contextTypes) {
1693
+ const componentName = getComponentNameFromType(Component) || 'Unknown';
1694
+ if (!didWarnAboutContextTypes[componentName]) {
1695
+ didWarnAboutContextTypes[componentName] = true;
1696
+ if (disableLegacyContext) {
1697
+ console.error(
1698
+ '%s uses the legacy contextTypes API which was removed in React 19. ' +
1699
+ 'Use React.createContext() with React.useContext() instead.',
1700
+ componentName,
1701
+ );
1702
+ } else {
1703
+ console.error(
1704
+ '%s uses the legacy contextTypes API which will be removed soon. ' +
1705
+ 'Use React.createContext() with React.useContext() instead.',
1706
+ componentName,
1707
+ );
1708
+ }
1709
+ }
1710
}
1711
}
1712
if (__DEV__) {
@@ -1771,14 +1784,12 @@ function finishFunctionComponent(
1784
1785
function validateFunctionComponentInDev(Component: any): void {
1786
if (__DEV__) {
1774
- if (Component) {
1775
- if (Component.childContextTypes) {
1776
- console.error(
1777
- 'childContextTypes cannot be defined on a function component.\n' +
1778
- ' %s.childContextTypes = ...',
1779
- Component.displayName || Component.name || 'Component',
1780
- );
1781
- }
1787
+ if (Component && Component.childContextTypes) {
1788
+ console.error(
1789
+ 'childContextTypes cannot be defined on a function component.\n' +
1790
+ ' %s.childContextTypes = ...',
1791
+ Component.displayName || Component.name || 'Component',
1792
+ );
1793
}
1794
1795
if (
packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
+14
-2
@@ -254,7 +254,12 @@ describe 'ReactCoffeeScriptClass', ->
254
render: ->
255
React.createElement Foo
256
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
+ ])
263
264
it 'renders only once when setting state in componentWillMount', ->
265
renderCount = 0
@@ -537,7 +542,14 @@ describe 'ReactCoffeeScriptClass', ->
542
render: ->
543
React.createElement Bar
544
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
+ )
553
554
if !featureFlags.disableStringRefs
555
it 'supports string refs', ->
packages/react/src/__tests__/ReactContextValidator-test.js
+39
-29
@@ -66,11 +66,16 @@ describe('ReactContextValidator', () => {
66
let instance;
67
const container = document.createElement('div');
68
const root = ReactDOMClient.createRoot(container);
69
- await act(() => {
70
- root.render(
71
- <ComponentInFooBarContext ref={current => (instance = current)} />,
72
- );
73
- });
69
+ await expect(async () => {
70
+ await act(() => {
71
+ root.render(
72
+ <ComponentInFooBarContext ref={current => (instance = current)} />,
73
+ );
74
+ });
75
+ }).toErrorDev([
76
+ 'ComponentInFooBarContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
77
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
78
+ ]);
79
expect(instance.childRef.current.context).toEqual({foo: 'abc'});
80
});
81
@@ -139,9 +144,14 @@ describe('ReactContextValidator', () => {
144
145
const container = document.createElement('div');
146
const root = ReactDOMClient.createRoot(container);
142
- await act(() => {
143
- root.render(<Parent foo="abc" />);
144
- });
147
+ await expect(async () => {
148
+ await act(() => {
149
+ root.render(<Parent foo="abc" />);
150
+ });
151
+ }).toErrorDev([
152
+ 'Parent uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
153
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
154
+ ]);
155
156
expect(constructorContext).toEqual({foo: 'abc'});
157
expect(renderContext).toEqual({foo: 'abc'});
@@ -187,11 +197,10 @@ describe('ReactContextValidator', () => {
197
await act(() => {
198
root.render(<ComponentA />);
199
});
190
- }).toErrorDev(
191
- 'ComponentA.childContextTypes is specified but there is no ' +
192
- 'getChildContext() method on the instance. You can either define ' +
193
- 'getChildContext() on ComponentA or remove childContextTypes from it.',
194
- );
200
+ }).toErrorDev([
201
+ 'ComponentA uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
202
+ '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.',
203
+ ]);
204
205
// Warnings should be deduped by component type
206
let container = document.createElement('div');
@@ -206,11 +215,10 @@ describe('ReactContextValidator', () => {
215
await act(() => {
216
root.render(<ComponentB />);
217
});
209
- }).toErrorDev(
210
- 'ComponentB.childContextTypes is specified but there is no ' +
211
- 'getChildContext() method on the instance. You can either define ' +
212
- 'getChildContext() on ComponentB or remove childContextTypes from it.',
213
- );
218
+ }).toErrorDev([
219
+ 'ComponentB uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
220
+ '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.',
221
+ ]);
222
});
223
224
// TODO (bvaughn) Remove this test and the associated behavior in the future.
@@ -259,9 +267,10 @@ describe('ReactContextValidator', () => {
267
root.render(<ParentContextProvider />);
268
});
269
}).toErrorDev([
262
- 'MiddleMissingContext.childContextTypes is specified but there is no ' +
263
- 'getChildContext() method on the instance. You can either define getChildContext() ' +
264
- 'on MiddleMissingContext or remove childContextTypes from it.',
270
+ 'ParentContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
271
+ 'MiddleMissingContext uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
272
+ '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.',
273
+ 'ChildContextConsumer uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
274
]);
275
expect(childContext.bar).toBeUndefined();
276
expect(childContext.foo).toBe('FOO');
@@ -435,10 +444,11 @@ describe('ReactContextValidator', () => {
444
</ParentContextProvider>,
445
);
446
});
438
- }).toErrorDev(
439
- 'ComponentA declares both contextTypes and contextType static properties. ' +
440
- 'The legacy contextTypes property will be ignored.',
441
- );
447
+ }).toErrorDev([
448
+ 'ParentContextProvider uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead',
449
+ 'ComponentA uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
450
+ 'ComponentA declares both contextTypes and contextType static properties. The legacy contextTypes property will be ignored.',
451
+ ]);
452
453
// Warnings should be deduped by component type
454
let container = document.createElement('div');
@@ -461,10 +471,10 @@ describe('ReactContextValidator', () => {
471
</ParentContextProvider>,
472
);
473
});
464
- }).toErrorDev(
465
- 'ComponentB declares both contextTypes and contextType static properties. ' +
466
- 'The legacy contextTypes property will be ignored.',
467
- );
474
+ }).toErrorDev([
475
+ 'ComponentB declares both contextTypes and contextType static properties. The legacy contextTypes property will be ignored.',
476
+ 'ComponentB uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
477
+ ]);
478
});
479
480
// @gate enableRenderableContext || !__DEV__
packages/react/src/__tests__/ReactES6Class-test.js
+11
@@ -13,6 +13,7 @@ let PropTypes;
13
let React;
14
let ReactDOM;
15
let ReactDOMClient;
16
+let assertConsoleErrorDev;
17
18
describe('ReactES6Class', () => {
19
let container;
@@ -30,6 +31,7 @@ describe('ReactES6Class', () => {
31
React = require('react');
32
ReactDOM = require('react-dom');
33
ReactDOMClient = require('react-dom/client');
34
+ ({assertConsoleErrorDev} = require('internal-test-utils'));
35
container = document.createElement('div');
36
root = ReactDOMClient.createRoot(container);
37
attachedListener = null;
@@ -287,6 +289,11 @@ describe('ReactES6Class', () => {
289
className: PropTypes.string,
290
};
291
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
+ ]);
297
});
298
}
299
@@ -579,6 +586,10 @@ describe('ReactES6Class', () => {
586
}
587
Foo.childContextTypes = {bar: PropTypes.string};
588
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
+ ]);
593
});
594
}
595
packages/react/src/__tests__/ReactStrictMode-test.js
+30
-7
@@ -18,6 +18,7 @@ let act;
18
let useMemo;
19
let useState;
20
let useReducer;
21
+let assertConsoleErrorDev;
22
23
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
24
@@ -28,7 +29,7 @@ describe('ReactStrictMode', () => {
29
ReactDOM = require('react-dom');
30
ReactDOMClient = require('react-dom/client');
31
ReactDOMServer = require('react-dom/server');
31
- act = require('internal-test-utils').act;
32
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
33
useMemo = React.useMemo;
34
useState = React.useState;
35
useReducer = React.useReducer;
@@ -1072,11 +1073,33 @@ describe('context legacy', () => {
1073
1074
const container = document.createElement('div');
1075
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
+ '(https://react.dev/link/legacy-context)' +
1084
+ '\n in LegacyContextProvider (at **)' +
1085
+ '\n in div (at **)' +
1086
+ '\n in Root (at **)',
1087
+ 'LegacyContextConsumer uses the legacy contextTypes API which ' +
1088
+ 'will soon be removed. Use React.createContext() with static ' +
1089
+ 'contextType instead. (https://react.dev/link/legacy-context)' +
1090
+ '\n in LegacyContextConsumer (at **)' +
1091
+ '\n in div (at **)' +
1092
+ '\n in LegacyContextProvider (at **)' +
1093
+ '\n in div (at **)' +
1094
+ '\n in Root (at **)',
1095
+ 'FunctionalLegacyContextConsumer uses the legacy contextTypes ' +
1096
+ 'API which will be removed soon. Use React.createContext() ' +
1097
+ 'with React.useContext() instead. (https://react.dev/link/legacy-context)' +
1098
+ '\n in FunctionalLegacyContextConsumer (at **)' +
1099
+ '\n in div (at **)' +
1100
+ '\n in LegacyContextProvider (at **)' +
1101
+ '\n in div (at **)' +
1102
+ '\n in Root (at **)',
1103
'Legacy context API has been detected within a strict-mode tree.' +
1104
'\n\nThe old API will be supported in all 16.x releases, but applications ' +
1105
'using it should migrate to the new version.' +
@@ -1087,7 +1110,7 @@ describe('context legacy', () => {
1110
'\n in LegacyContextProvider (at **)' +
1111
'\n in div (at **)' +
1112
'\n in Root (at **)',
1090
- );
1113
+ ]);
1114
1115
// Dedupe
1116
await act(() => {
packages/react/src/__tests__/ReactTypeScriptClass-test.ts
+9
-3
@@ -518,7 +518,10 @@ describe('ReactTypeScriptClass', function() {
518
519
if (!ReactFeatureFlags.disableLegacyContext) {
520
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
+ ]);
525
});
526
}
527
@@ -687,8 +690,11 @@ describe('ReactTypeScriptClass', function() {
690
});
691
692
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
+] );
698
});
699
}
700
packages/react/src/__tests__/createReactClassIntegration-test.js
+6
-1
@@ -10,6 +10,7 @@
10
'use strict';
11
12
let act;
13
+let assertConsoleErrorDev;
14
15
let PropTypes;
16
let React;
@@ -19,7 +20,7 @@ let createReactClass;
20
describe('create-react-class-integration', () => {
21
beforeEach(() => {
22
jest.resetModules();
22
- ({act} = require('internal-test-utils'));
23
+ ({act, assertConsoleErrorDev} = require('internal-test-utils'));
24
PropTypes = require('prop-types');
25
React = require('react');
26
ReactDOMClient = require('react-dom/client');
@@ -336,6 +337,10 @@ describe('create-react-class-integration', () => {
337
await act(() => {
338
root.render(<Outer />);
339
});
340
+ assertConsoleErrorDev([
341
+ 'Component uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
342
+ 'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
343
+ ]);
344
expect(container.firstChild.className).toBe('foo');
345
});
346