Skip to content

Commit

Permalink
feat: honor displayName of context types (#18224)
Browse files Browse the repository at this point in the history
* Revert "Revert "feat: honor displayName of context types (#18035)" (#18223)"

This reverts commit 3ee812e.

* Add warning of displayName is set on the consumer

* dedupe warning
  • Loading branch information
Brian Vaughn committed Mar 5, 2020
1 parent 3ee812e commit d35f8a5
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 2 deletions.
15 changes: 15 additions & 0 deletions packages/react/src/ReactContext.js
Expand Up @@ -57,6 +57,7 @@ export function createContext<T>(

let hasWarnedAboutUsingNestedContextConsumers = false;
let hasWarnedAboutUsingConsumerProvider = false;
let hasWarnedAboutDisplayNameOnConsumer = false;

if (__DEV__) {
// A separate object, but proxies back to the original context object for
Expand Down Expand Up @@ -120,6 +121,20 @@ export function createContext<T>(
return context.Consumer;
},
},
displayName: {
get() {
return context.displayName;
},
set() {
if (!hasWarnedAboutDisplayNameOnConsumer) {
console.warn(
'Setting `displayName` on Context.Consumer has no effect. ' +
"You should set it directly on the context with Context.displayName = 'NamedContext'.",
);
hasWarnedAboutDisplayNameOnConsumer = true;
}
},
},
});
// $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
context.Consumer = Consumer;
Expand Down
39 changes: 39 additions & 0 deletions packages/react/src/__tests__/ReactContextValidator-test.js
Expand Up @@ -18,6 +18,7 @@
let PropTypes;
let React;
let ReactDOM;
let ReactDOMServer;
let ReactTestUtils;

describe('ReactContextValidator', () => {
Expand All @@ -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');
});

Expand Down Expand Up @@ -671,4 +673,41 @@ 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(
<Context.Provider>
<Context.Consumer>{() => <Validator />}</Context.Consumer>
</Context.Provider>,
);
}).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 **)',
);
});

it('warns if displayName is set on the consumer type', () => {
const Context = React.createContext(null);

expect(() => {
Context.Consumer.displayName = 'ignored';
}).toWarnDev(
'Warning: Setting `displayName` on Context.Consumer has no effect. ' +
"You should set it directly on the context with Context.displayName = 'NamedContext'.",
{withoutStack: true},
);

// warning is deduped so subsequent setting is fine
Context.Consumer.displayName = 'ignored';
});
});
11 changes: 9 additions & 2 deletions packages/shared/getComponentName.js
Expand Up @@ -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,
Expand All @@ -37,6 +38,10 @@ function getWrappedName(
);
}

function getContextName(type: ReactContext<any>) {
return type.displayName || 'Context';
}

function getComponentName(type: mixed): string | null {
if (type == null) {
// Host root, text node or just invalid type.
Expand Down Expand Up @@ -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<any> = (type: any);
return getContextName(context) + '.Consumer';
case REACT_PROVIDER_TYPE:
return 'Context.Provider';
const provider: ReactProviderType<any> = (type: any);
return getContextName(provider._context) + '.Provider';
case REACT_FORWARD_REF_TYPE:
return getWrappedName(type, type.render, 'ForwardRef');
case REACT_MEMO_TYPE:
Expand Down

0 comments on commit d35f8a5

Please sign in to comment.