diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js
index af9a6095e0a46..ee2afb4fae371 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js
@@ -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 = () => (
+
+
+
+
+ {theme => {theme}
}
+
+
+
+
+ );
+ // We expect 1 error.
+ await render(, 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 = () => (
+
+
+
+
+ {theme => {theme}
}
+
+
+
+
+ );
+ // We expect 0 errors.
+ await render(, 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 (
+
+
+
+
+
+ {theme => {theme}
}
+
+
+
+
+
+ );
+ };
+ // We expect 1 error.
+ await render(, 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 (
+
+
+
+
+
+ {theme => {theme}
}
+
+
+
+
+
+ );
+ };
+ // We expect 1 error.
+ await render(, 1);
+ },
+ );
});
});
diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js
index 1ee885c68abc2..a905f972ebe3f 100644
--- a/packages/react-dom/src/server/ReactPartialRenderer.js
+++ b/packages/react-dom/src/server/ReactPartialRenderer.js
@@ -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';
@@ -91,6 +87,7 @@ let validatePropertiesInDevelopment = (type, props) => {};
let pushCurrentDebugStack = (stack: Array) => {};
let pushElementToDebugStack = (element: ReactElement) => {};
let popCurrentDebugStack = () => {};
+let hasWarnedAboutUsingContextAsConsumer = false;
if (__DEV__) {
ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
@@ -1054,9 +1051,35 @@ class ReactDOMServerRenderer {
return '';
}
case REACT_CONTEXT_TYPE: {
- const consumer: ReactConsumer = (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 directly is not supported and will be removed in ' +
+ 'a future major release. Did you mean to render 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 = {