@samitouri / QOS-React / commits / 96fe3b1be2

Add React.isValidElementType() (#12483)

* Add React.isValidElementType() Per the conversation on #12453, there are a number of third-party libraries (particularly those that generate higher-order components) that are performing suboptimal validation of element types. This commit exposes a function that can perform the desired check without depending upon React internals. * Move isValidElementType to shared/

James Reggio committed Mar 29, 2018 at 14:45 UTC 96fe3b1be2fe74e83c9a25d7511f23dbef15ac99
4 files changed +75 -21
packages/react-is/src/ReactIs.js
+3
@@ -19,6 +19,7 @@ import {
19 REACT_PROVIDER_TYPE,
20 REACT_STRICT_MODE_TYPE,
21 } from 'shared/ReactSymbols';
22 +import isValidElementType from 'shared/isValidElementType';
23
24 export function typeOf(object: any) {
25 if (typeof object === 'object' && object !== null) {
@@ -62,6 +63,8 @@ export const Fragment = REACT_FRAGMENT_TYPE;
63 export const Portal = REACT_PORTAL_TYPE;
64 export const StrictMode = REACT_STRICT_MODE_TYPE;
65
66 +export {isValidElementType};
67 +
68 export function isAsyncMode(object: any) {
69 return typeOf(object) === REACT_ASYNC_MODE_TYPE;
70 }
packages/react-is/src/__tests__/ReactIs-test.js
+36
@@ -31,6 +31,42 @@ describe('ReactIs', () => {
31 expect(ReactIs.typeOf(undefined)).toBe(undefined);
32 });
33
34 + it('identifies valid element types', () => {
35 + class Component extends React.Component {
36 + render() {
37 + return React.createElement('div');
38 + }
39 + }
40 +
41 + const StatelessComponent = () => React.createElement('div');
42 +
43 + const ForwardRefComponent = React.forwardRef((props, ref) =>
44 + React.createElement(Component, {forwardedRef: ref, ...props}),
45 + );
46 +
47 + const Context = React.createContext(false);
48 +
49 + expect(ReactIs.isValidElementType('div')).toEqual(true);
50 + expect(ReactIs.isValidElementType(Component)).toEqual(true);
51 + expect(ReactIs.isValidElementType(StatelessComponent)).toEqual(true);
52 + expect(ReactIs.isValidElementType(ForwardRefComponent)).toEqual(true);
53 + expect(ReactIs.isValidElementType(Context.Provider)).toEqual(true);
54 + expect(ReactIs.isValidElementType(Context.Consumer)).toEqual(true);
55 + expect(ReactIs.isValidElementType(React.createFactory('div'))).toEqual(
56 + true,
57 + );
58 + expect(ReactIs.isValidElementType(React.Fragment)).toEqual(true);
59 + expect(ReactIs.isValidElementType(React.unstable_AsyncMode)).toEqual(true);
60 + expect(ReactIs.isValidElementType(React.StrictMode)).toEqual(true);
61 +
62 + expect(ReactIs.isValidElementType(true)).toEqual(false);
63 + expect(ReactIs.isValidElementType(123)).toEqual(false);
64 + expect(ReactIs.isValidElementType({})).toEqual(false);
65 + expect(ReactIs.isValidElementType(null)).toEqual(false);
66 + expect(ReactIs.isValidElementType(undefined)).toEqual(false);
67 + expect(ReactIs.isValidElementType({type: 'div', props: {}})).toEqual(false);
68 + });
69 +
70 it('should identify async mode', () => {
71 expect(ReactIs.typeOf(<React.unstable_AsyncMode />)).toBe(
72 ReactIs.AsyncMode,
packages/react/src/ReactElementValidator.js
+3 -21
@@ -14,16 +14,9 @@
14
15 import lowPriorityWarning from 'shared/lowPriorityWarning';
16 import describeComponentFrame from 'shared/describeComponentFrame';
17 +import isValidElementType from 'shared/isValidElementType';
18 import getComponentName from 'shared/getComponentName';
18 -import {
19 - getIteratorFn,
20 - REACT_FRAGMENT_TYPE,
21 - REACT_STRICT_MODE_TYPE,
22 - REACT_ASYNC_MODE_TYPE,
23 - REACT_PROVIDER_TYPE,
24 - REACT_CONTEXT_TYPE,
25 - REACT_FORWARD_REF_TYPE,
26 -} from 'shared/ReactSymbols';
19 +import {getIteratorFn, REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
20 import checkPropTypes from 'prop-types/checkPropTypes';
21 import warning from 'fbjs/lib/warning';
22
@@ -288,18 +281,7 @@ function validateFragmentProps(fragment) {
281 }
282
283 export function createElementWithValidation(type, props, children) {
291 - const validType =
292 - typeof type === 'string' ||
293 - typeof type === 'function' ||
294 - // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
295 - type === REACT_FRAGMENT_TYPE ||
296 - type === REACT_ASYNC_MODE_TYPE ||
297 - type === REACT_STRICT_MODE_TYPE ||
298 - (typeof type === 'object' &&
299 - type !== null &&
300 - (type.$$typeof === REACT_PROVIDER_TYPE ||
301 - type.$$typeof === REACT_CONTEXT_TYPE ||
302 - type.$$typeof === REACT_FORWARD_REF_TYPE));
284 + const validType = isValidElementType(type);
285
286 // We warn in this case but don't throw. We expect the element creation to
287 // succeed and there will likely be errors in render.
packages/shared/isValidElementType.js new
+33
@@ -0,0 +1,33 @@
1 +/**
2 + * Copyright (c) 2016-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import {
11 + REACT_FRAGMENT_TYPE,
12 + REACT_ASYNC_MODE_TYPE,
13 + REACT_STRICT_MODE_TYPE,
14 + REACT_PROVIDER_TYPE,
15 + REACT_CONTEXT_TYPE,
16 + REACT_FORWARD_REF_TYPE,
17 +} from 'shared/ReactSymbols';
18 +
19 +export default function isValidElementType(type: mixed) {
20 + return (
21 + typeof type === 'string' ||
22 + typeof type === 'function' ||
23 + // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
24 + type === REACT_FRAGMENT_TYPE ||
25 + type === REACT_ASYNC_MODE_TYPE ||
26 + type === REACT_STRICT_MODE_TYPE ||
27 + (typeof type === 'object' &&
28 + type !== null &&
29 + (type.$$typeof === REACT_PROVIDER_TYPE ||
30 + type.$$typeof === REACT_CONTEXT_TYPE ||
31 + type.$$typeof === REACT_FORWARD_REF_TYPE))
32 + );
33 +}