Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline react-is utils to fix tree-shaking in 9.0 #2085

Merged
merged 4 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@
"import": "./dist/react-redux.alternate-renderers.mjs"
}
},
"sideEffects": "false",
"sideEffects": false,
"files": [
"dist",
"es"
"dist"
],
"scripts": {
"build": "tsup",
Expand Down Expand Up @@ -72,7 +71,6 @@
},
"dependencies": {
"@types/use-sync-external-store": "^0.0.3",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
},
"devDependencies": {
Expand All @@ -87,17 +85,15 @@
"@babel/preset-env": "^7.12.1",
"@babel/preset-typescript": "^7.14.5",
"@microsoft/api-extractor": "^7.18.1",
"@reduxjs/toolkit": "^2.0.0-beta.0",
"@reduxjs/toolkit": "^2.0.0-beta.4",
"@testing-library/jest-dom": "^5.11.5",
"@testing-library/jest-native": "^3.4.3",
"@testing-library/react": "13.0.0",
"@testing-library/react-12": "npm:@testing-library/react@^12",
"@testing-library/react-hooks": "^3.4.2",
"@testing-library/react-native": "^7.1.0",
"@types/object-assign": "^4.0.30",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-is": "^17",
"@types/react-native": "^0.67.4",
"@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0",
Expand Down
45 changes: 27 additions & 18 deletions src/components/connect.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */
import type { ComponentType } from 'react'
import * as React from 'react'
import { isValidElementType, isContextConsumer } from 'react-is'
import { isValidElementType, isContextConsumer } from '../utils/react-is'

import type { Store } from 'redux'

Expand Down Expand Up @@ -491,15 +491,14 @@ function connect<
type WrappedComponentProps = TProps &
ConnectPropsMaybeWithoutContext<TProps>

if (
process.env.NODE_ENV !== 'production' &&
!isValidElementType(WrappedComponent)
) {
throw new Error(
`You must pass a component to the function returned by connect. Instead received ${stringifyComponent(
WrappedComponent
)}`
)
if (process.env.NODE_ENV !== 'production') {
const isValid = /*#__PURE__*/ isValidElementType(WrappedComponent)
if (!isValid)
throw new Error(
`You must pass a component to the function returned by connect. Instead received ${stringifyComponent(
WrappedComponent
)}`
)
}

const wrappedComponentName =
Expand Down Expand Up @@ -544,12 +543,22 @@ function connect<
const ContextToUse: ReactReduxContextInstance = React.useMemo(() => {
// Users may optionally pass in a custom context instance to use instead of our ReactReduxContext.
// Memoize the check that determines which context instance we should use.
return propsContext &&
propsContext.Consumer &&
// @ts-ignore
isContextConsumer(<propsContext.Consumer />)
? propsContext
: Context
let ResultContext = Context
if (propsContext?.Consumer) {
if (process.env.NODE_ENV !== 'production') {
const isValid = /*#__PURE__*/ isContextConsumer(
// @ts-ignore
<propsContext.Consumer />
)
if (!isValid) {
throw new Error(
'You must pass a valid React context consumer as `props.context`'
)
}
ResultContext = propsContext
}
}
return ResultContext
}, [propsContext, Context])

// Retrieve the store and ancestor subscription via context, if available
Expand Down Expand Up @@ -797,10 +806,10 @@ function connect<
const forwarded = _forwarded as ConnectedWrapperComponent
forwarded.displayName = displayName
forwarded.WrappedComponent = WrappedComponent
return hoistStatics(forwarded, WrappedComponent)
return /*#__PURE__*/ hoistStatics(forwarded, WrappedComponent)
}

return hoistStatics(Connect, WrappedComponent)
return /*#__PURE__*/ hoistStatics(Connect, WrappedComponent)
}

return wrapWithConnect
Expand Down
2 changes: 1 addition & 1 deletion src/utils/hoistStatics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
import type * as React from 'react'
import { ForwardRef, Memo, isMemo } from 'react-is'
import { ForwardRef, Memo, isMemo } from '../utils/react-is'

const REACT_STATICS = {
childContextTypes: true,
Expand Down
113 changes: 113 additions & 0 deletions src/utils/react-is.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type { ElementType, MemoExoticComponent, ReactElement } from 'react'

// Directly ported from:
// https://unpkg.com/browse/react-is@18.3.0-canary-ee68446ff-20231115/cjs/react-is.production.js
// It's very possible this could change in the future, but given that
// we only use these in `connect`, this is a low priority.

const REACT_ELEMENT_TYPE = Symbol.for('react.element')
const REACT_PORTAL_TYPE = Symbol.for('react.portal')
const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment')
const REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode')
const REACT_PROFILER_TYPE = Symbol.for('react.profiler')
const REACT_PROVIDER_TYPE = Symbol.for('react.provider')
const REACT_CONTEXT_TYPE = Symbol.for('react.context')
const REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context')
const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref')
const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense')
const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list')
const REACT_MEMO_TYPE = Symbol.for('react.memo')
const REACT_LAZY_TYPE = Symbol.for('react.lazy')
const REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen')
const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference')

export const ForwardRef = REACT_FORWARD_REF_TYPE
export const Memo = REACT_MEMO_TYPE

export function isValidElementType(type: any): type is ElementType {
if (typeof type === 'string' || typeof type === 'function') {
return true
} // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).

if (
type === REACT_FRAGMENT_TYPE ||
type === REACT_PROFILER_TYPE ||
type === REACT_STRICT_MODE_TYPE ||
type === REACT_SUSPENSE_TYPE ||
type === REACT_SUSPENSE_LIST_TYPE ||
type === REACT_OFFSCREEN_TYPE
) {
return true
}

if (typeof type === 'object' && type !== null) {
if (
type.$$typeof === REACT_LAZY_TYPE ||
type.$$typeof === REACT_MEMO_TYPE ||
type.$$typeof === REACT_PROVIDER_TYPE ||
type.$$typeof === REACT_CONTEXT_TYPE ||
type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
// types supported by any Flight configuration anywhere since
// we don't know which Flight build this will end up being used
// with.
type.$$typeof === REACT_CLIENT_REFERENCE ||
type.getModuleId !== undefined
) {
return true
}
}

return false
}

function typeOf(object: any): symbol | undefined {
if (typeof object === 'object' && object !== null) {
const $$typeof = object.$$typeof

switch ($$typeof) {
case REACT_ELEMENT_TYPE: {
const type = object.type

switch (type) {
case REACT_FRAGMENT_TYPE:
case REACT_PROFILER_TYPE:
case REACT_STRICT_MODE_TYPE:
case REACT_SUSPENSE_TYPE:
case REACT_SUSPENSE_LIST_TYPE:
return type

default: {
const $$typeofType = type && type.$$typeof

switch ($$typeofType) {
case REACT_SERVER_CONTEXT_TYPE:
case REACT_CONTEXT_TYPE:
case REACT_FORWARD_REF_TYPE:
case REACT_LAZY_TYPE:
case REACT_MEMO_TYPE:
case REACT_PROVIDER_TYPE:
return $$typeofType

default:
return $$typeof
}
}
}
}

case REACT_PORTAL_TYPE: {
return $$typeof
}
}
}

return undefined
}

export function isContextConsumer(object: any): object is ReactElement {
return typeOf(object) === REACT_CONTEXT_TYPE
}

export function isMemo(object: any): object is MemoExoticComponent<any> {
return typeOf(object) === REACT_MEMO_TYPE
}
2 changes: 1 addition & 1 deletion test/typetests/react-redux-types.typetest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
fetchCount,
} from './counterApp'

import objectAssign from 'object-assign'
const objectAssign = Object.assign

class Counter extends Component<any, any> {
render() {
Expand Down
57 changes: 19 additions & 38 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2419,23 +2419,23 @@ __metadata:
languageName: node
linkType: hard

"@reduxjs/toolkit@npm:^2.0.0-beta.0":
version: 2.0.0-beta.0
resolution: "@reduxjs/toolkit@npm:2.0.0-beta.0"
"@reduxjs/toolkit@npm:^2.0.0-beta.4":
version: 2.0.0-beta.4
resolution: "@reduxjs/toolkit@npm:2.0.0-beta.4"
dependencies:
immer: ^10.0.2
redux: 5.0.0-beta.0
redux-thunk: 3.0.0-alpha.3
reselect: ^5.0.0-alpha.2
redux: ^5.0.0-beta.0
redux-thunk: ^3.0.0-beta.0
reselect: ^5.0.0-beta.0
peerDependencies:
react: ^16.9.0 || ^17.0.0 || ^18
react-redux: ^7.2.1 || ^8.0.2
react-redux: ^7.2.1 || ^8.0.2 || ^9.0.0-beta.0
peerDependenciesMeta:
react:
optional: true
react-redux:
optional: true
checksum: e03ecca1ef61d1073908095cb43215b3f8254c0a81872d44b0f49788ade72bf303bffd43dee57a8f8acebf76fade7e0bfb3d049189aed4467cb98b2e5e7c5b01
checksum: f7fe690b26840485a0dbc4a367424fc6c96604d8f6cab17ccb216ce1320d9a5c2f81c13d4e93d14095de6b0196ac742bff43b4834494be258ee42559e9dd429c
languageName: node
linkType: hard

Expand Down Expand Up @@ -2785,13 +2785,6 @@ __metadata:
languageName: node
linkType: hard

"@types/object-assign@npm:^4.0.30":
version: 4.0.30
resolution: "@types/object-assign@npm:4.0.30"
checksum: 24e0471ddcd578b7ea72d5174e9cd6b68d78b5fa00f9f48cee38713c0e2886c6c3478c53c04d0508d16deb4370eed71ed0bb1f5b9aaa406e61f07ffed5da1d3b
languageName: node
linkType: hard

"@types/prettier@npm:^2.1.5":
version: 2.7.3
resolution: "@types/prettier@npm:2.7.3"
Expand All @@ -2815,15 +2808,6 @@ __metadata:
languageName: node
linkType: hard

"@types/react-is@npm:^17":
version: 17.0.3
resolution: "@types/react-is@npm:17.0.3"
dependencies:
"@types/react": "*"
checksum: 6abb7c47d54f012272650df8a962a28bd82f219291e5ef8b4dfa7fe0bb98ae243b060bf9fbe8ceff6213141794855a006db194b490b00ffd15842ae19d0ce1f0
languageName: node
linkType: hard

"@types/react-native@npm:^0.67.4":
version: 0.67.4
resolution: "@types/react-native@npm:0.67.4"
Expand Down Expand Up @@ -9381,17 +9365,15 @@ __metadata:
"@babel/preset-env": ^7.12.1
"@babel/preset-typescript": ^7.14.5
"@microsoft/api-extractor": ^7.18.1
"@reduxjs/toolkit": ^2.0.0-beta.0
"@reduxjs/toolkit": ^2.0.0-beta.4
"@testing-library/jest-dom": ^5.11.5
"@testing-library/jest-native": ^3.4.3
"@testing-library/react": 13.0.0
"@testing-library/react-12": "npm:@testing-library/react@^12"
"@testing-library/react-hooks": ^3.4.2
"@testing-library/react-native": ^7.1.0
"@types/object-assign": ^4.0.30
"@types/react": ^18
"@types/react-dom": ^18
"@types/react-is": ^17
"@types/react-native": ^0.67.4
"@types/use-sync-external-store": ^0.0.3
"@typescript-eslint/eslint-plugin": ^4.28.0
Expand All @@ -9412,7 +9394,6 @@ __metadata:
prettier: ^2.1.2
react: 18.2.0
react-dom: 18.2.0
react-is: ^18.0.0
react-native: ^0.71.11
react-test-renderer: 18.0.0
redux: ^5.0.0-beta.0
Expand Down Expand Up @@ -9589,16 +9570,16 @@ __metadata:
languageName: node
linkType: hard

"redux-thunk@npm:3.0.0-alpha.3":
version: 3.0.0-alpha.3
resolution: "redux-thunk@npm:3.0.0-alpha.3"
"redux-thunk@npm:^3.0.0-beta.0":
version: 3.0.0-beta.0
resolution: "redux-thunk@npm:3.0.0-beta.0"
peerDependencies:
redux: ^4
checksum: a5be77887b422b3182ff7fae617ec552cd5f830afb326d83af32a430c3eb439c942a38c3691e5c975119e37787974172dbc0139f7782cbfaeea5c1292fa123ed
redux: ^4 || ^5.0.0-beta.0
checksum: 1609e18a9fb56ab7403d760999996b50e136fcf7411ec9d809e9a4afa4187bf0ab545652c05ffbfca2e0397e59e6baf2ae0d35631a30bf8ba20af1205e98e0fe
languageName: node
linkType: hard

"redux@npm:5.0.0-beta.0, redux@npm:^5.0.0-beta.0":
"redux@npm:^5.0.0-beta.0":
version: 5.0.0-beta.0
resolution: "redux@npm:5.0.0-beta.0"
checksum: 11df373e219f2f515ee1bda1a19a1ba5de02d8d5c874800ec353179dcd106eddd54432946fd0ab37c47f99f8fe53f820a6404c14da7f039a46022187e9469d2d
Expand Down Expand Up @@ -9738,10 +9719,10 @@ __metadata:
languageName: node
linkType: hard

"reselect@npm:^5.0.0-alpha.2":
version: 5.0.0-alpha.2
resolution: "reselect@npm:5.0.0-alpha.2"
checksum: c47b66999800e1297721cbc4b2464b520fade9823c598d578759c9fba3eb6be03b184e13c20f30820cc18fe2688fc9fb4475f83e59d8f2347aa0d591e465637d
"reselect@npm:^5.0.0-beta.0":
version: 5.0.0-beta.0
resolution: "reselect@npm:5.0.0-beta.0"
checksum: 462363aa730af93e396ff0d885f88fb8c43572b07f51c2a890d37f27edc3afecd300085916533e336142b3883f8532f35b5b1a2aaa1a70e9909aea48e5d3b98f
languageName: node
linkType: hard

Expand Down
Loading