honor displayName set on ForwardRef if available (#13615)
honor displayName set on ForwardRef if available (#13615) * add failing test * honor displayName set on ForwardRef if available Since React.forwardRef returns a component object, some users (including styled-components and react-native) are starting to decorate them with various statics including displayName. This adjusts React's various name-getters to honor this if set and surface the name in warnings and hopefully DevTools. * fix typing * Refine later
Evan Jacobs committed
Sep 11, 2018 at 14:19 UTC
e49f3ca08e0dc8ac050f92b45a7734fa60d86e22
3 files changed
+36
-4
packages/react/src/ReactElementValidator.js
+3
-1
@@ -197,7 +197,9 @@ function validatePropTypes(element) {
197
) {
198
// ForwardRef
199
const functionName = type.render.displayName || type.render.name || '';
200
- name = functionName !== '' ? `ForwardRef(${functionName})` : 'ForwardRef';
200
+ name =
201
+ type.displayName ||
202
+ (functionName !== '' ? `ForwardRef(${functionName})` : 'ForwardRef');
203
propTypes = type.propTypes;
204
} else {
205
return;
packages/react/src/__tests__/forwardRef-test.js
+29
@@ -181,4 +181,33 @@ describe('forwardRef', () => {
181
{withoutStack: true},
182
);
183
});
184
+
185
+ it('should honor a displayName if set on the forwardRef wrapper in warnings', () => {
186
+ const Component = props => <div {...props} />;
187
+
188
+ const RefForwardingComponent = React.forwardRef((props, ref) => (
189
+ <Component {...props} forwardedRef={ref} />
190
+ ));
191
+
192
+ RefForwardingComponent.displayName = 'Foo';
193
+
194
+ RefForwardingComponent.propTypes = {
195
+ optional: PropTypes.string,
196
+ required: PropTypes.string.isRequired,
197
+ };
198
+
199
+ RefForwardingComponent.defaultProps = {
200
+ optional: 'default',
201
+ };
202
+
203
+ const ref = React.createRef();
204
+
205
+ expect(() =>
206
+ ReactNoop.render(<RefForwardingComponent ref={ref} optional="foo" />),
207
+ ).toWarnDev(
208
+ 'Warning: Failed prop type: The prop `required` is marked as required in ' +
209
+ '`Foo`, but its value is `undefined`.\n' +
210
+ ' in Foo (at **)',
211
+ );
212
+ });
213
});
packages/shared/getComponentName.js
+4
-3
@@ -66,9 +66,10 @@ function getComponentName(type: mixed): string | null {
66
case REACT_FORWARD_REF_TYPE:
67
const renderFn = (type.render: any);
68
const functionName = renderFn.displayName || renderFn.name || '';
69
- return functionName !== ''
70
- ? `ForwardRef(${functionName})`
71
- : 'ForwardRef';
69
+ return (
70
+ (type: any).displayName ||
71
+ (functionName !== '' ? `ForwardRef(${functionName})` : 'ForwardRef')
72
+ );
73
}
74
if (typeof type.then === 'function') {
75
const thenable: Thenable<mixed> = (type: any);