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

* Applies context object warnings to ReactDOM server
This commit is contained in:
Dominic Gannaway 2018-11-07 17:19:38 +00:00 committed by GitHub
parent e3a7b96455
commit aa1ffe4e77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 130 additions and 8 deletions

View File

@ -249,5 +249,104 @@ describe('ReactDOMServerIntegration', () => {
expect(e.querySelector('#language2').textContent).toBe('sanskrit');
expect(e.querySelector('#language3').textContent).toBe('french');
});
itRenders(
'should warn with an error message when using Context as consumer in DEV',
async render => {
const Theme = React.createContext('dark');
const Language = React.createContext('french');
const App = () => (
<div>
<Theme.Provider value="light">
<Language.Provider value="english">
<Theme.Provider value="dark">
<Theme>{theme => <div id="theme1">{theme}</div>}</Theme>
</Theme.Provider>
</Language.Provider>
</Theme.Provider>
</div>
);
// We expect 1 error.
await render(<App />, 1);
},
);
// False positive regression test.
itRenders(
'should not warn when using Consumer from React < 16.6 with newer renderer',
async render => {
const Theme = React.createContext('dark');
const Language = React.createContext('french');
// React 16.5 and earlier didn't have a separate object.
Theme.Consumer = Theme;
const App = () => (
<div>
<Theme.Provider value="light">
<Language.Provider value="english">
<Theme.Provider value="dark">
<Theme>{theme => <div id="theme1">{theme}</div>}</Theme>
</Theme.Provider>
</Language.Provider>
</Theme.Provider>
</div>
);
// We expect 0 errors.
await render(<App />, 0);
},
);
itRenders(
'should warn with an error message when using nested context consumers in DEV',
async render => {
const App = () => {
const Theme = React.createContext('dark');
const Language = React.createContext('french');
return (
<div>
<Theme.Provider value="light">
<Language.Provider value="english">
<Theme.Provider value="dark">
<Theme.Consumer.Consumer>
{theme => <div id="theme1">{theme}</div>}
</Theme.Consumer.Consumer>
</Theme.Provider>
</Language.Provider>
</Theme.Provider>
</div>
);
};
// We expect 1 error.
await render(<App />, 1);
},
);
itRenders(
'should warn with an error message when using Context.Consumer.Provider DEV',
async render => {
const App = () => {
const Theme = React.createContext('dark');
const Language = React.createContext('french');
return (
<div>
<Theme.Provider value="light">
<Language.Provider value="english">
<Theme.Consumer.Provider value="dark">
<Theme.Consumer>
{theme => <div id="theme1">{theme}</div>}
</Theme.Consumer>
</Theme.Consumer.Provider>
</Language.Provider>
</Theme.Provider>
</div>
);
};
// We expect 1 error.
await render(<App />, 1);
},
);
});
});

View File

@ -8,11 +8,7 @@
*/
import type {ReactElement} from 'shared/ReactElementType';
import type {
ReactProvider,
ReactConsumer,
ReactContext,
} from 'shared/ReactTypes';
import type {ReactProvider, ReactContext} from 'shared/ReactTypes';
import React from 'react';
import invariant from 'shared/invariant';
@ -93,6 +89,7 @@ let validatePropertiesInDevelopment = (type, props) => {};
let pushCurrentDebugStack = (stack: Array<Frame>) => {};
let pushElementToDebugStack = (element: ReactElement) => {};
let popCurrentDebugStack = () => {};
let hasWarnedAboutUsingContextAsConsumer = false;
if (__DEV__) {
ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
@ -1061,9 +1058,35 @@ class ReactDOMServerRenderer {
return '';
}
case REACT_CONTEXT_TYPE: {
const consumer: ReactConsumer<any> = (nextChild: any);
const nextProps: any = consumer.props;
const nextValue = consumer.type._currentValue;
let reactContext = (nextChild: any).type;
// The logic below for Context differs depending on PROD or DEV mode. In
// DEV mode, we create a separate object for Context.Consumer that acts
// like a proxy to Context. This proxy object adds unnecessary code in PROD
// so we use the old behaviour (Context.Consumer references Context) to
// reduce size and overhead. The separate object references context via
// a property called "_context", which also gives us the ability to check
// in DEV mode if this property exists or not and warn if it does not.
if (__DEV__) {
if ((reactContext: any)._context === undefined) {
// This may be because it's a Context (rather than a Consumer).
// Or it may be because it's older React where they're the same thing.
// We only want to warn if we're sure it's a new React.
if (reactContext !== reactContext.Consumer) {
if (!hasWarnedAboutUsingContextAsConsumer) {
hasWarnedAboutUsingContextAsConsumer = true;
warning(
false,
'Rendering <Context> directly is not supported and will be removed in ' +
'a future major release. Did you mean to render <Context.Consumer> instead?',
);
}
}
} else {
reactContext = (reactContext: any)._context;
}
}
const nextProps: any = (nextChild: any).props;
const nextValue = reactContext._currentValue;
const nextChildren = toArray(nextProps.children(nextValue));
const frame: Frame = {