Skip to content

Commit adbfd54

Browse files
committed
Inline react-is utils
1 parent f37ee48 commit adbfd54

File tree

3 files changed

+141
-19
lines changed

3 files changed

+141
-19
lines changed

src/components/connect.tsx

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */
22
import type { ComponentType } from 'react'
33
import * as React from 'react'
4-
import { isValidElementType, isContextConsumer } from 'react-is'
4+
import { isValidElementType, isContextConsumer } from '../utils/react-is'
55

66
import type { Store } from 'redux'
77

@@ -491,15 +491,14 @@ function connect<
491491
type WrappedComponentProps = TProps &
492492
ConnectPropsMaybeWithoutContext<TProps>
493493

494-
if (
495-
process.env.NODE_ENV !== 'production' &&
496-
!isValidElementType(WrappedComponent)
497-
) {
498-
throw new Error(
499-
`You must pass a component to the function returned by connect. Instead received ${stringifyComponent(
500-
WrappedComponent
501-
)}`
502-
)
494+
if (process.env.NODE_ENV !== 'production') {
495+
const isValid = /*#__PURE__*/ isValidElementType(WrappedComponent)
496+
if (!isValid)
497+
throw new Error(
498+
`You must pass a component to the function returned by connect. Instead received ${stringifyComponent(
499+
WrappedComponent
500+
)}`
501+
)
503502
}
504503

505504
const wrappedComponentName =
@@ -544,12 +543,22 @@ function connect<
544543
const ContextToUse: ReactReduxContextInstance = React.useMemo(() => {
545544
// Users may optionally pass in a custom context instance to use instead of our ReactReduxContext.
546545
// Memoize the check that determines which context instance we should use.
547-
return propsContext &&
548-
propsContext.Consumer &&
549-
// @ts-ignore
550-
isContextConsumer(<propsContext.Consumer />)
551-
? propsContext
552-
: Context
546+
let ResultContext = Context
547+
if (propsContext?.Consumer) {
548+
if (process.env.NODE_ENV !== 'production') {
549+
const isValid = /*#__PURE__*/ isContextConsumer(
550+
// @ts-ignore
551+
<propsContext.Consumer />
552+
)
553+
if (!isValid) {
554+
throw new Error(
555+
'You must pass a valid React context consumer as `props.context`'
556+
)
557+
}
558+
ResultContext = propsContext
559+
}
560+
}
561+
return ResultContext
553562
}, [propsContext, Context])
554563

555564
// Retrieve the store and ancestor subscription via context, if available
@@ -797,10 +806,10 @@ function connect<
797806
const forwarded = _forwarded as ConnectedWrapperComponent
798807
forwarded.displayName = displayName
799808
forwarded.WrappedComponent = WrappedComponent
800-
return hoistStatics(forwarded, WrappedComponent)
809+
return /*#__PURE__*/ hoistStatics(forwarded, WrappedComponent)
801810
}
802811

803-
return hoistStatics(Connect, WrappedComponent)
812+
return /*#__PURE__*/ hoistStatics(Connect, WrappedComponent)
804813
}
805814

806815
return wrapWithConnect

src/utils/hoistStatics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
88
*/
99
import type * as React from 'react'
10-
import { ForwardRef, Memo, isMemo } from 'react-is'
10+
import { ForwardRef, Memo, isMemo } from '../utils/react-is'
1111

1212
const REACT_STATICS = {
1313
childContextTypes: true,

src/utils/react-is.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import type { ElementType, MemoExoticComponent, ReactElement } from 'react'
2+
3+
// Directly ported from:
4+
// https://unpkg.com/browse/[email protected]/cjs/react-is.production.js
5+
// It's very possible this could change in the future, but given that
6+
// we only use these in `connect`, this is a low priority.
7+
8+
const REACT_ELEMENT_TYPE = Symbol.for('react.element')
9+
const REACT_PORTAL_TYPE = Symbol.for('react.portal')
10+
const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment')
11+
const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode')
12+
const REACT_PROFILER_TYPE = Symbol.for('react.profiler')
13+
const REACT_PROVIDER_TYPE = Symbol.for('react.provider')
14+
const REACT_CONTEXT_TYPE = Symbol.for('react.context')
15+
const REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context')
16+
const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref')
17+
const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense')
18+
const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list')
19+
const REACT_MEMO_TYPE = Symbol.for('react.memo')
20+
const REACT_LAZY_TYPE = Symbol.for('react.lazy')
21+
const REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen')
22+
const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference')
23+
24+
export const ForwardRef = REACT_FORWARD_REF_TYPE
25+
export const Memo = REACT_MEMO_TYPE
26+
27+
export function isValidElementType(type: any): type is ElementType {
28+
if (typeof type === 'string' || typeof type === 'function') {
29+
return true
30+
} // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).
31+
32+
if (
33+
type === REACT_FRAGMENT_TYPE ||
34+
type === REACT_PROFILER_TYPE ||
35+
type === REACT_STRICT_MODE_TYPE ||
36+
type === REACT_SUSPENSE_TYPE ||
37+
type === REACT_SUSPENSE_LIST_TYPE ||
38+
type === REACT_OFFSCREEN_TYPE
39+
) {
40+
return true
41+
}
42+
43+
if (typeof type === 'object' && type !== null) {
44+
if (
45+
type.$$typeof === REACT_LAZY_TYPE ||
46+
type.$$typeof === REACT_MEMO_TYPE ||
47+
type.$$typeof === REACT_PROVIDER_TYPE ||
48+
type.$$typeof === REACT_CONTEXT_TYPE ||
49+
type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
50+
// types supported by any Flight configuration anywhere since
51+
// we don't know which Flight build this will end up being used
52+
// with.
53+
type.$$typeof === REACT_CLIENT_REFERENCE ||
54+
type.getModuleId !== undefined
55+
) {
56+
return true
57+
}
58+
}
59+
60+
return false
61+
}
62+
63+
function typeOf(object: any): symbol | undefined {
64+
if (typeof object === 'object' && object !== null) {
65+
const $$typeof = object.$$typeof
66+
67+
switch ($$typeof) {
68+
case REACT_ELEMENT_TYPE: {
69+
const type = object.type
70+
71+
switch (type) {
72+
case REACT_FRAGMENT_TYPE:
73+
case REACT_PROFILER_TYPE:
74+
case REACT_STRICT_MODE_TYPE:
75+
case REACT_SUSPENSE_TYPE:
76+
case REACT_SUSPENSE_LIST_TYPE:
77+
return type
78+
79+
default: {
80+
const $$typeofType = type && type.$$typeof
81+
82+
switch ($$typeofType) {
83+
case REACT_SERVER_CONTEXT_TYPE:
84+
case REACT_CONTEXT_TYPE:
85+
case REACT_FORWARD_REF_TYPE:
86+
case REACT_LAZY_TYPE:
87+
case REACT_MEMO_TYPE:
88+
case REACT_PROVIDER_TYPE:
89+
return $$typeofType
90+
91+
default:
92+
return $$typeof
93+
}
94+
}
95+
}
96+
}
97+
98+
case REACT_PORTAL_TYPE: {
99+
return $$typeof
100+
}
101+
}
102+
}
103+
104+
return undefined
105+
}
106+
107+
export function isContextConsumer(object: any): object is ReactElement {
108+
return typeOf(object) === REACT_CONTEXT_TYPE
109+
}
110+
111+
export function isMemo(object: any): object is MemoExoticComponent<any> {
112+
return typeOf(object) === REACT_MEMO_TYPE
113+
}

0 commit comments

Comments
 (0)