diff --git a/packages/react/src/ReactContext.js b/packages/react/src/ReactContext.js index cbcd3373c8166..af97b903cf734 100644 --- a/packages/react/src/ReactContext.js +++ b/packages/react/src/ReactContext.js @@ -120,6 +120,11 @@ export function createContext( return context.Consumer; }, }, + displayName: { + get() { + return context.displayName; + }, + }, }); // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty context.Consumer = Consumer; diff --git a/packages/react/src/__tests__/ReactContextValidator-test.js b/packages/react/src/__tests__/ReactContextValidator-test.js index 0123d7091755f..f751dc70727e0 100644 --- a/packages/react/src/__tests__/ReactContextValidator-test.js +++ b/packages/react/src/__tests__/ReactContextValidator-test.js @@ -18,6 +18,7 @@ let PropTypes; let React; let ReactDOM; +let ReactDOMServer; let ReactTestUtils; describe('ReactContextValidator', () => { @@ -27,6 +28,7 @@ describe('ReactContextValidator', () => { PropTypes = require('prop-types'); React = require('react'); ReactDOM = require('react-dom'); + ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); }); @@ -671,4 +673,26 @@ describe('ReactContextValidator', () => { 'Warning: ComponentB: Function components do not support contextType.', ); }); + + it('should honor a displayName if set on the context type', () => { + const Context = React.createContext(null); + Context.displayName = 'MyContextType'; + function Validator() { + return null; + } + Validator.propTypes = {dontPassToSeeErrorStack: PropTypes.bool.isRequired}; + + expect(() => { + ReactDOMServer.renderToStaticMarkup( + + {() => } + , + ); + }).toErrorDev( + 'Warning: Failed prop type: The prop `dontPassToSeeErrorStack` is marked as required in `Validator`, but its value is `undefined`.\n' + + ' in Validator (at **)\n' + + ' in MyContextType.Consumer (at **)\n' + + ' in MyContextType.Provider (at **)', + ); + }); }); diff --git a/packages/shared/getComponentName.js b/packages/shared/getComponentName.js index e2ad538e584a9..9694732ce5d96 100644 --- a/packages/shared/getComponentName.js +++ b/packages/shared/getComponentName.js @@ -24,6 +24,7 @@ import { REACT_BLOCK_TYPE, } from 'shared/ReactSymbols'; import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent'; +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes'; function getWrappedName( outerType: mixed, @@ -37,6 +38,10 @@ function getWrappedName( ); } +function getContextName(type: ReactContext) { + return type.displayName || 'Context'; +} + function getComponentName(type: mixed): string | null { if (type == null) { // Host root, text node or just invalid type. @@ -73,9 +78,11 @@ function getComponentName(type: mixed): string | null { if (typeof type === 'object') { switch (type.$$typeof) { case REACT_CONTEXT_TYPE: - return 'Context.Consumer'; + const context: ReactContext = (type: any); + return getContextName(context) + '.Consumer'; case REACT_PROVIDER_TYPE: - return 'Context.Provider'; + const provider: ReactProviderType = (type: any); + return getContextName(provider._context) + '.Provider'; case REACT_FORWARD_REF_TYPE: return getWrappedName(type, type.render, 'ForwardRef'); case REACT_MEMO_TYPE: