Skip to content

Commit d35f8a5

Browse files
author
Brian Vaughn
authored
feat: honor displayName of context types (#18224)
* 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
1 parent 3ee812e commit d35f8a5

File tree

3 files changed

+63
-2
lines changed

3 files changed

+63
-2
lines changed

packages/react/src/ReactContext.js

+15
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export function createContext<T>(
5757

5858
let hasWarnedAboutUsingNestedContextConsumers = false;
5959
let hasWarnedAboutUsingConsumerProvider = false;
60+
let hasWarnedAboutDisplayNameOnConsumer = false;
6061

6162
if (__DEV__) {
6263
// A separate object, but proxies back to the original context object for
@@ -120,6 +121,20 @@ export function createContext<T>(
120121
return context.Consumer;
121122
},
122123
},
124+
displayName: {
125+
get() {
126+
return context.displayName;
127+
},
128+
set() {
129+
if (!hasWarnedAboutDisplayNameOnConsumer) {
130+
console.warn(
131+
'Setting `displayName` on Context.Consumer has no effect. ' +
132+
"You should set it directly on the context with Context.displayName = 'NamedContext'.",
133+
);
134+
hasWarnedAboutDisplayNameOnConsumer = true;
135+
}
136+
},
137+
},
123138
});
124139
// $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
125140
context.Consumer = Consumer;

packages/react/src/__tests__/ReactContextValidator-test.js

+39
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
let PropTypes;
1919
let React;
2020
let ReactDOM;
21+
let ReactDOMServer;
2122
let ReactTestUtils;
2223

2324
describe('ReactContextValidator', () => {
@@ -27,6 +28,7 @@ describe('ReactContextValidator', () => {
2728
PropTypes = require('prop-types');
2829
React = require('react');
2930
ReactDOM = require('react-dom');
31+
ReactDOMServer = require('react-dom/server');
3032
ReactTestUtils = require('react-dom/test-utils');
3133
});
3234

@@ -671,4 +673,41 @@ describe('ReactContextValidator', () => {
671673
'Warning: ComponentB: Function components do not support contextType.',
672674
);
673675
});
676+
677+
it('should honor a displayName if set on the context type', () => {
678+
const Context = React.createContext(null);
679+
Context.displayName = 'MyContextType';
680+
function Validator() {
681+
return null;
682+
}
683+
Validator.propTypes = {dontPassToSeeErrorStack: PropTypes.bool.isRequired};
684+
685+
expect(() => {
686+
ReactDOMServer.renderToStaticMarkup(
687+
<Context.Provider>
688+
<Context.Consumer>{() => <Validator />}</Context.Consumer>
689+
</Context.Provider>,
690+
);
691+
}).toErrorDev(
692+
'Warning: Failed prop type: The prop `dontPassToSeeErrorStack` is marked as required in `Validator`, but its value is `undefined`.\n' +
693+
' in Validator (at **)\n' +
694+
' in MyContextType.Consumer (at **)\n' +
695+
' in MyContextType.Provider (at **)',
696+
);
697+
});
698+
699+
it('warns if displayName is set on the consumer type', () => {
700+
const Context = React.createContext(null);
701+
702+
expect(() => {
703+
Context.Consumer.displayName = 'ignored';
704+
}).toWarnDev(
705+
'Warning: Setting `displayName` on Context.Consumer has no effect. ' +
706+
"You should set it directly on the context with Context.displayName = 'NamedContext'.",
707+
{withoutStack: true},
708+
);
709+
710+
// warning is deduped so subsequent setting is fine
711+
Context.Consumer.displayName = 'ignored';
712+
});
674713
});

packages/shared/getComponentName.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
REACT_BLOCK_TYPE,
2525
} from 'shared/ReactSymbols';
2626
import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent';
27+
import type {ReactContext, ReactProviderType} from 'shared/ReactTypes';
2728

2829
function getWrappedName(
2930
outerType: mixed,
@@ -37,6 +38,10 @@ function getWrappedName(
3738
);
3839
}
3940

41+
function getContextName(type: ReactContext<any>) {
42+
return type.displayName || 'Context';
43+
}
44+
4045
function getComponentName(type: mixed): string | null {
4146
if (type == null) {
4247
// Host root, text node or just invalid type.
@@ -73,9 +78,11 @@ function getComponentName(type: mixed): string | null {
7378
if (typeof type === 'object') {
7479
switch (type.$$typeof) {
7580
case REACT_CONTEXT_TYPE:
76-
return 'Context.Consumer';
81+
const context: ReactContext<any> = (type: any);
82+
return getContextName(context) + '.Consumer';
7783
case REACT_PROVIDER_TYPE:
78-
return 'Context.Provider';
84+
const provider: ReactProviderType<any> = (type: any);
85+
return getContextName(provider._context) + '.Provider';
7986
case REACT_FORWARD_REF_TYPE:
8087
return getWrappedName(type, type.render, 'ForwardRef');
8188
case REACT_MEMO_TYPE:

0 commit comments

Comments
 (0)