diff --git a/packages/react-dom/src/__tests__/ReactServerRendering-test.js b/packages/react-dom/src/__tests__/ReactServerRendering-test.js index 14e14fccc5e4..738148ba3b64 100644 --- a/packages/react-dom/src/__tests__/ReactServerRendering-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRendering-test.js @@ -904,4 +904,43 @@ describe('ReactDOMServer', () => { ' in App (at **)', ]); }); + + it('should warn if an invalid contextType is defined', () => { + const Context = React.createContext(); + + class ComponentA extends React.Component { + // It should warn for both Context.Consumer and Context.Provider + static contextType = Context.Consumer; + render() { + return
; + } + } + class ComponentB extends React.Component { + static contextType = Context.Provider; + render() { + return
; + } + } + + expect(() => { + ReactDOMServer.renderToString(); + }).toWarnDev( + 'Warning: ComponentA defines an invalid contextType. ' + + 'contextType should point to the Context object returned by React.createContext(). ' + + 'Did you accidentally pass the Context.Consumer instead?', + {withoutStack: true}, + ); + + // Warnings should be deduped by component type + ReactDOMServer.renderToString(); + + expect(() => { + ReactDOMServer.renderToString(); + }).toWarnDev( + 'Warning: ComponentB defines an invalid contextType. ' + + 'contextType should point to the Context object returned by React.createContext(). ' + + 'Did you accidentally pass the Context.Provider instead?', + {withoutStack: true}, + ); + }); }); diff --git a/packages/react-dom/src/server/ReactPartialRendererContext.js b/packages/react-dom/src/server/ReactPartialRendererContext.js index d7ceaa09758f..93c857d28371 100644 --- a/packages/react-dom/src/server/ReactPartialRendererContext.js +++ b/packages/react-dom/src/server/ReactPartialRendererContext.js @@ -77,7 +77,10 @@ export function processContext( const contextType = type.contextType; if (typeof contextType === 'object' && contextType !== null) { if (__DEV__) { - if (contextType.$$typeof !== REACT_CONTEXT_TYPE) { + const isContextConsumer = + contextType.$$typeof === REACT_CONTEXT_TYPE && + contextType._context !== undefined; + if (contextType.$$typeof !== REACT_CONTEXT_TYPE || isContextConsumer) { let name = getComponentName(type) || 'Component'; if (!didWarnAboutInvalidateContextType[name]) { didWarnAboutInvalidateContextType[name] = true; @@ -85,8 +88,9 @@ export function processContext( false, '%s defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext(). ' + - 'Did you accidentally pass the Context.Provider instead?', + 'Did you accidentally pass the Context.%s instead?', name, + isContextConsumer ? 'Consumer' : 'Provider', ); } } diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 3875371714f3..40c7aca28b9a 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -515,8 +515,11 @@ function constructClassInstance( const contextType = ctor.contextType; if (typeof contextType === 'object' && contextType !== null) { if (__DEV__) { + const isContextConsumer = + contextType.$$typeof === REACT_CONTEXT_TYPE && + contextType._context !== undefined; if ( - contextType.$$typeof !== REACT_CONTEXT_TYPE && + (contextType.$$typeof !== REACT_CONTEXT_TYPE || isContextConsumer) && !didWarnAboutInvalidateContextType.has(ctor) ) { didWarnAboutInvalidateContextType.add(ctor); @@ -524,8 +527,9 @@ function constructClassInstance( false, '%s defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext(). ' + - 'Did you accidentally pass the Context.Provider instead?', + 'Did you accidentally pass the Context.%s instead?', getComponentName(ctor) || 'Component', + isContextConsumer ? 'Consumer' : 'Provider', ); } } diff --git a/packages/react/src/__tests__/ReactContextValidator-test.js b/packages/react/src/__tests__/ReactContextValidator-test.js index 7ee787e2250d..b01f008bacb1 100644 --- a/packages/react/src/__tests__/ReactContextValidator-test.js +++ b/packages/react/src/__tests__/ReactContextValidator-test.js @@ -541,9 +541,10 @@ describe('ReactContextValidator', () => { it('should warn if an invalid contextType is defined', () => { const Context = React.createContext(); - + // This tests that both Context.Consumer and Context.Provider + // warn about invalid contextType. class ComponentA extends React.Component { - static contextType = Context.Provider; + static contextType = Context.Consumer; render() { return
; } @@ -560,7 +561,7 @@ describe('ReactContextValidator', () => { }).toWarnDev( 'Warning: ComponentA defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext(). ' + - 'Did you accidentally pass the Context.Provider instead?', + 'Did you accidentally pass the Context.Consumer instead?', {withoutStack: true}, );