@samitouri / QOS-React / commits / d35f8a5818

feat: honor displayName of context types (#18224)

* Revert "Revert "feat: honor displayName of context types (#18035)" (#18223)" This reverts commit 3ee812e6b67d52a6f262144e78721f9340c36a04. * Add warning of displayName is set on the consumer * dedupe warning

Brian Vaughn committed Mar 5, 2020 at 10:13 UTC d35f8a58184d1495ff54f4ad80c702e9468c89ee
3 files changed +63 -2
packages/react/src/ReactContext.js
+15
@@ -57,6 +57,7 @@ export function createContext<T>(
57
58 let hasWarnedAboutUsingNestedContextConsumers = false;
59 let hasWarnedAboutUsingConsumerProvider = false;
60 + let hasWarnedAboutDisplayNameOnConsumer = false;
61
62 if (__DEV__) {
63 // A separate object, but proxies back to the original context object for
@@ -120,6 +121,20 @@ export function createContext<T>(
121 return context.Consumer;
122 },
123 },
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 + },
138 });
139 // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
140 context.Consumer = Consumer;
packages/react/src/__tests__/ReactContextValidator-test.js
+39
@@ -18,6 +18,7 @@
18 let PropTypes;
19 let React;
20 let ReactDOM;
21 +let ReactDOMServer;
22 let ReactTestUtils;
23
24 describe('ReactContextValidator', () => {
@@ -27,6 +28,7 @@ describe('ReactContextValidator', () => {
28 PropTypes = require('prop-types');
29 React = require('react');
30 ReactDOM = require('react-dom');
31 + ReactDOMServer = require('react-dom/server');
32 ReactTestUtils = require('react-dom/test-utils');
33 });
34
@@ -671,4 +673,41 @@ describe('ReactContextValidator', () => {
673 'Warning: ComponentB: Function components do not support contextType.',
674 );
675 });
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 + });
713 });
packages/shared/getComponentName.js
+9 -2
@@ -24,6 +24,7 @@ import {
24 REACT_BLOCK_TYPE,
25 } from 'shared/ReactSymbols';
26 import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent';
27 +import type {ReactContext, ReactProviderType} from 'shared/ReactTypes';
28
29 function getWrappedName(
30 outerType: mixed,
@@ -37,6 +38,10 @@ function getWrappedName(
38 );
39 }
40
41 +function getContextName(type: ReactContext<any>) {
42 + return type.displayName || 'Context';
43 +}
44 +
45 function getComponentName(type: mixed): string | null {
46 if (type == null) {
47 // Host root, text node or just invalid type.
@@ -73,9 +78,11 @@ function getComponentName(type: mixed): string | null {
78 if (typeof type === 'object') {
79 switch (type.$$typeof) {
80 case REACT_CONTEXT_TYPE:
76 - return 'Context.Consumer';
81 + const context: ReactContext<any> = (type: any);
82 + return getContextName(context) + '.Consumer';
83 case REACT_PROVIDER_TYPE:
78 - return 'Context.Provider';
84 + const provider: ReactProviderType<any> = (type: any);
85 + return getContextName(provider._context) + '.Provider';
86 case REACT_FORWARD_REF_TYPE:
87 return getWrappedName(type, type.render, 'ForwardRef');
88 case REACT_MEMO_TYPE: