@samitouri / QOS-React / commits / aa1ffe4e77

Show deprecated context object warnings usage in ReactDOM server (#14033)

* Applies context object warnings to ReactDOM server

Dominic Gannaway committed Nov 7, 2018 at 17:19 UTC aa1ffe4e779b8a6da4317be619ff4f0d3d7d02bb
2 files changed +130 -8
packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js
+99
@@ -249,5 +249,104 @@ describe('ReactDOMServerIntegration', () => {
249 expect(e.querySelector('#language2').textContent).toBe('sanskrit');
250 expect(e.querySelector('#language3').textContent).toBe('french');
251 });
252 +
253 + itRenders(
254 + 'should warn with an error message when using Context as consumer in DEV',
255 + async render => {
256 + const Theme = React.createContext('dark');
257 + const Language = React.createContext('french');
258 +
259 + const App = () => (
260 + <div>
261 + <Theme.Provider value="light">
262 + <Language.Provider value="english">
263 + <Theme.Provider value="dark">
264 + <Theme>{theme => <div id="theme1">{theme}</div>}</Theme>
265 + </Theme.Provider>
266 + </Language.Provider>
267 + </Theme.Provider>
268 + </div>
269 + );
270 + // We expect 1 error.
271 + await render(<App />, 1);
272 + },
273 + );
274 +
275 + // False positive regression test.
276 + itRenders(
277 + 'should not warn when using Consumer from React < 16.6 with newer renderer',
278 + async render => {
279 + const Theme = React.createContext('dark');
280 + const Language = React.createContext('french');
281 + // React 16.5 and earlier didn't have a separate object.
282 + Theme.Consumer = Theme;
283 +
284 + const App = () => (
285 + <div>
286 + <Theme.Provider value="light">
287 + <Language.Provider value="english">
288 + <Theme.Provider value="dark">
289 + <Theme>{theme => <div id="theme1">{theme}</div>}</Theme>
290 + </Theme.Provider>
291 + </Language.Provider>
292 + </Theme.Provider>
293 + </div>
294 + );
295 + // We expect 0 errors.
296 + await render(<App />, 0);
297 + },
298 + );
299 +
300 + itRenders(
301 + 'should warn with an error message when using nested context consumers in DEV',
302 + async render => {
303 + const App = () => {
304 + const Theme = React.createContext('dark');
305 + const Language = React.createContext('french');
306 +
307 + return (
308 + <div>
309 + <Theme.Provider value="light">
310 + <Language.Provider value="english">
311 + <Theme.Provider value="dark">
312 + <Theme.Consumer.Consumer>
313 + {theme => <div id="theme1">{theme}</div>}
314 + </Theme.Consumer.Consumer>
315 + </Theme.Provider>
316 + </Language.Provider>
317 + </Theme.Provider>
318 + </div>
319 + );
320 + };
321 + // We expect 1 error.
322 + await render(<App />, 1);
323 + },
324 + );
325 +
326 + itRenders(
327 + 'should warn with an error message when using Context.Consumer.Provider DEV',
328 + async render => {
329 + const App = () => {
330 + const Theme = React.createContext('dark');
331 + const Language = React.createContext('french');
332 +
333 + return (
334 + <div>
335 + <Theme.Provider value="light">
336 + <Language.Provider value="english">
337 + <Theme.Consumer.Provider value="dark">
338 + <Theme.Consumer>
339 + {theme => <div id="theme1">{theme}</div>}
340 + </Theme.Consumer>
341 + </Theme.Consumer.Provider>
342 + </Language.Provider>
343 + </Theme.Provider>
344 + </div>
345 + );
346 + };
347 + // We expect 1 error.
348 + await render(<App />, 1);
349 + },
350 + );
351 });
352 });
packages/react-dom/src/server/ReactPartialRenderer.js
+31 -8
@@ -8,11 +8,7 @@
8 */
9
10 import type {ReactElement} from 'shared/ReactElementType';
11 -import type {
12 - ReactProvider,
13 - ReactConsumer,
14 - ReactContext,
15 -} from 'shared/ReactTypes';
11 +import type {ReactProvider, ReactContext} from 'shared/ReactTypes';
12
13 import React from 'react';
14 import invariant from 'shared/invariant';
@@ -93,6 +89,7 @@ let validatePropertiesInDevelopment = (type, props) => {};
89 let pushCurrentDebugStack = (stack: Array<Frame>) => {};
90 let pushElementToDebugStack = (element: ReactElement) => {};
91 let popCurrentDebugStack = () => {};
92 +let hasWarnedAboutUsingContextAsConsumer = false;
93
94 if (__DEV__) {
95 ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
@@ -1061,9 +1058,35 @@ class ReactDOMServerRenderer {
1058 return '';
1059 }
1060 case REACT_CONTEXT_TYPE: {
1064 - const consumer: ReactConsumer<any> = (nextChild: any);
1065 - const nextProps: any = consumer.props;
1066 - const nextValue = consumer.type._currentValue;
1061 + let reactContext = (nextChild: any).type;
1062 + // The logic below for Context differs depending on PROD or DEV mode. In
1063 + // DEV mode, we create a separate object for Context.Consumer that acts
1064 + // like a proxy to Context. This proxy object adds unnecessary code in PROD
1065 + // so we use the old behaviour (Context.Consumer references Context) to
1066 + // reduce size and overhead. The separate object references context via
1067 + // a property called "_context", which also gives us the ability to check
1068 + // in DEV mode if this property exists or not and warn if it does not.
1069 + if (__DEV__) {
1070 + if ((reactContext: any)._context === undefined) {
1071 + // This may be because it's a Context (rather than a Consumer).
1072 + // Or it may be because it's older React where they're the same thing.
1073 + // We only want to warn if we're sure it's a new React.
1074 + if (reactContext !== reactContext.Consumer) {
1075 + if (!hasWarnedAboutUsingContextAsConsumer) {
1076 + hasWarnedAboutUsingContextAsConsumer = true;
1077 + warning(
1078 + false,
1079 + 'Rendering <Context> directly is not supported and will be removed in ' +
1080 + 'a future major release. Did you mean to render <Context.Consumer> instead?',
1081 + );
1082 + }
1083 + }
1084 + } else {
1085 + reactContext = (reactContext: any)._context;
1086 + }
1087 + }
1088 + const nextProps: any = (nextChild: any).props;
1089 + const nextValue = reactContext._currentValue;
1090
1091 const nextChildren = toArray(nextProps.children(nextValue));
1092 const frame: Frame = {