Skip to content

Commit 4a9ba60

Browse files
authored
Merge pull request #2176 from aryaemami59/improve-treeshakeability
Improve treeshakeability of build artifacts
2 parents 560ac02 + 49f8136 commit 4a9ba60

16 files changed

+1532
-1751
lines changed

Diff for: .eslintrc.json

+8-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
},
2020
"plugins": ["@typescript-eslint", "import", "react"],
2121
"rules": {
22-
"valid-jsdoc": [2],
22+
"valid-jsdoc": [
23+
2,
24+
{ "requireReturnType": false, "requireParamType": false }
25+
],
2326
"react/no-is-mounted": [0]
2427
},
2528
"settings": {
@@ -55,7 +58,10 @@
5558
{ "fixStyle": "separate-type-imports" }
5659
],
5760
"@typescript-eslint/consistent-type-exports": [2],
58-
"valid-jsdoc": [2],
61+
"valid-jsdoc": [
62+
2,
63+
{ "requireReturnType": false, "requireParamType": false }
64+
],
5965
"react/no-is-mounted": [0]
6066
}
6167
},

Diff for: package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
},
3434
"sideEffects": false,
3535
"files": [
36-
"dist"
36+
"dist",
37+
"src"
3738
],
3839
"scripts": {
3940
"build": "yarn clean && tsup",
@@ -42,7 +43,7 @@
4243
"format": "prettier --write \"{src,test}/**/*.{js,ts,tsx}\" \"docs/**/*.md\"",
4344
"lint": "eslint src test",
4445
"lint:fix": "eslint src test --fix",
45-
"prepare": "yarn clean && yarn build",
46+
"prepack": "yarn build",
4647
"pretest": "yarn lint",
4748
"test": "vitest --run --typecheck",
4849
"test:watch": "vitest --watch",
@@ -102,7 +103,7 @@
102103
"react-test-renderer": "18.3.1",
103104
"redux": "^5.0.1",
104105
"rimraf": "^5.0.7",
105-
"tsup": "7.0.0",
106+
"tsup": "^8.3.5",
106107
"typescript": "^5.5.4",
107108
"typescript-eslint": "^7.12.0",
108109
"vitest": "^1.6.0"

Diff for: src/components/Context.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface ReactReduxContextValue<
1313
getServerState?: () => SS
1414
}
1515

16-
const ContextKey = Symbol.for(`react-redux-context`)
16+
const ContextKey = /* @__PURE__ */ Symbol.for(`react-redux-context`)
1717
const gT: {
1818
[ContextKey]?: Map<
1919
typeof React.createContext,
@@ -48,5 +48,3 @@ function getContext(): Context<ReactReduxContextValue | null> {
4848
export const ReactReduxContext = /*#__PURE__*/ getContext()
4949

5050
export type ReactReduxContextInstance = typeof ReactReduxContext
51-
52-
export default ReactReduxContext

Diff for: src/components/Provider.tsx

+20-12
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,32 @@ export interface ProviderProps<
5454
children: ReactNode
5555
}
5656

57-
function Provider<A extends Action<string> = UnknownAction, S = unknown>({
58-
store,
59-
context,
60-
children,
61-
serverState,
62-
stabilityCheck = 'once',
63-
identityFunctionCheck = 'once',
64-
}: ProviderProps<A, S>) {
57+
function Provider<A extends Action<string> = UnknownAction, S = unknown>(
58+
providerProps: ProviderProps<A, S>,
59+
) {
60+
const { children, context, serverState, store } = providerProps
61+
6562
const contextValue = React.useMemo(() => {
6663
const subscription = createSubscription(store)
67-
return {
64+
65+
const baseContextValue = {
6866
store,
6967
subscription,
7068
getServerState: serverState ? () => serverState : undefined,
71-
stabilityCheck,
72-
identityFunctionCheck,
7369
}
74-
}, [store, serverState, stabilityCheck, identityFunctionCheck])
70+
71+
if (process.env.NODE_ENV === 'production') {
72+
return baseContextValue
73+
} else {
74+
const { identityFunctionCheck = 'once', stabilityCheck = 'once' } =
75+
providerProps
76+
77+
return /* @__PURE__ */ Object.assign(baseContextValue, {
78+
stabilityCheck,
79+
identityFunctionCheck,
80+
})
81+
}
82+
}, [store, serverState])
7583

7684
const previousState = React.useMemo(() => store.getState(), [store])
7785

Diff for: src/components/connect.tsx

+1-9
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,6 @@ import type {
3939
} from './Context'
4040
import { ReactReduxContext } from './Context'
4141

42-
import type { uSES } from '../utils/useSyncExternalStore'
43-
import { notInitialized } from '../utils/useSyncExternalStore'
44-
45-
let useSyncExternalStore = notInitialized as uSES
46-
export const initializeConnect = (fn: uSES) => {
47-
useSyncExternalStore = fn
48-
}
49-
5042
// Define some constant arrays just to avoid re-creating these
5143
const EMPTY_ARRAY: [unknown, number] = [null, 0]
5244
const NO_SUBSCRIPTION_ARRAY = [null, null]
@@ -726,7 +718,7 @@ function connect<
726718
let actualChildProps: Record<string, unknown>
727719

728720
try {
729-
actualChildProps = useSyncExternalStore(
721+
actualChildProps = React.useSyncExternalStore(
730722
// TODO We're passing through a big wrapper that does a bunch of extra side effects besides subscribing
731723
subscribeForReact,
732724
// TODO This is incredibly hacky. We've already processed the store update and calculated new child props,

Diff for: src/connect/mergeProps.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createInvalidArgFactory } from './invalidArgFactory'
44
import type { MergeProps } from './selectorFactory'
55
import type { EqualityFn } from '../types'
66

7-
export function defaultMergeProps<
7+
function defaultMergeProps<
88
TStateProps,
99
TDispatchProps,
1010
TOwnProps,
@@ -18,7 +18,7 @@ export function defaultMergeProps<
1818
return { ...ownProps, ...stateProps, ...dispatchProps }
1919
}
2020

21-
export function wrapMergePropsFunc<
21+
function wrapMergePropsFunc<
2222
TStateProps,
2323
TDispatchProps,
2424
TOwnProps,

Diff for: src/connect/selectorFactory.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ interface PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State> {
6464
readonly areOwnPropsEqual: EqualityFn<TOwnProps>
6565
}
6666

67-
export function pureFinalPropsSelectorFactory<
67+
function pureFinalPropsSelectorFactory<
6868
TStateProps,
6969
TOwnProps,
7070
TDispatchProps,

Diff for: src/connect/wrapMapToProps.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function wrapMapToPropsConstant(
4747
// A length of zero is assumed to mean mapToProps is getting args via arguments or ...args and
4848
// therefore not reporting its length accurately..
4949
// TODO Can this get pulled out so that we can subscribe directly to the store if we don't need ownProps?
50-
export function getDependsOnOwnProps(mapToProps: MapToProps) {
50+
function getDependsOnOwnProps(mapToProps: MapToProps) {
5151
return mapToProps.dependsOnOwnProps
5252
? Boolean(mapToProps.dependsOnOwnProps)
5353
: mapToProps.length !== 1

Diff for: src/hooks/useSelector.ts

+11-17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
//import * as React from 'react'
22
import { React } from '../utils/react'
3-
3+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'
44
import type { ReactReduxContextValue } from '../components/Context'
55
import { ReactReduxContext } from '../components/Context'
66
import type { EqualityFn, NoInfer } from '../types'
7-
import type { uSESWS } from '../utils/useSyncExternalStore'
8-
import { notInitialized } from '../utils/useSyncExternalStore'
97
import {
108
createReduxContextHook,
119
useReduxContext as useDefaultReduxContext,
@@ -118,11 +116,6 @@ export interface UseSelector<StateType = unknown> {
118116
>() => UseSelector<OverrideStateType>
119117
}
120118

121-
let useSyncExternalStoreWithSelector = notInitialized as uSESWS
122-
export const initializeUseSelector = (fn: uSESWS) => {
123-
useSyncExternalStoreWithSelector = fn
124-
}
125-
126119
const refEquality: EqualityFn<any> = (a, b) => a === b
127120

128121
/**
@@ -148,7 +141,7 @@ export function createSelectorHook(
148141
| EqualityFn<NoInfer<Selected>>
149142
| UseSelectorOptions<NoInfer<Selected>> = {},
150143
): Selected => {
151-
const { equalityFn = refEquality, devModeChecks = {} } =
144+
const { equalityFn = refEquality } =
152145
typeof equalityFnOrOptions === 'function'
153146
? { equalityFn: equalityFnOrOptions }
154147
: equalityFnOrOptions
@@ -166,13 +159,9 @@ export function createSelectorHook(
166159
}
167160
}
168161

169-
const {
170-
store,
171-
subscription,
172-
getServerState,
173-
stabilityCheck,
174-
identityFunctionCheck,
175-
} = useReduxContext()
162+
const reduxContext = useReduxContext()
163+
164+
const { store, subscription, getServerState } = reduxContext
176165

177166
const firstRun = React.useRef(true)
178167

@@ -181,6 +170,11 @@ export function createSelectorHook(
181170
[selector.name](state: TState) {
182171
const selected = selector(state)
183172
if (process.env.NODE_ENV !== 'production') {
173+
const { devModeChecks = {} } =
174+
typeof equalityFnOrOptions === 'function'
175+
? {}
176+
: equalityFnOrOptions
177+
const { identityFunctionCheck, stabilityCheck } = reduxContext
184178
const {
185179
identityFunctionCheck: finalIdentityFunctionCheck,
186180
stabilityCheck: finalStabilityCheck,
@@ -243,7 +237,7 @@ export function createSelectorHook(
243237
return selected
244238
},
245239
}[selector.name],
246-
[selector, stabilityCheck, devModeChecks.stabilityCheck],
240+
[selector],
247241
)
248242

249243
const selectedState = useSyncExternalStoreWithSelector(

Diff for: src/index.ts

-14
Original file line numberDiff line numberDiff line change
@@ -1,15 +1 @@
1-
// The primary entry point assumes we are working with React 18, and thus have
2-
// useSyncExternalStore available. We can import that directly from React itself.
3-
// The useSyncExternalStoreWithSelector has to be imported, but we can use the
4-
// non-shim version. This shaves off the byte size of the shim.
5-
6-
import * as React from 'react'
7-
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'
8-
9-
import { initializeUseSelector } from './hooks/useSelector'
10-
import { initializeConnect } from './components/connect'
11-
12-
initializeUseSelector(useSyncExternalStoreWithSelector)
13-
initializeConnect(React.useSyncExternalStore)
14-
151
export * from './exports'

Diff for: src/utils/react-is.ts

+21-15
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,27 @@ import type { ElementType, MemoExoticComponent, ReactElement } from 'react'
55
// It's very possible this could change in the future, but given that
66
// we only use these in `connect`, this is a low priority.
77

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')
8+
const REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for('react.element')
9+
const REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for('react.portal')
10+
const REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for('react.fragment')
11+
const REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for('react.strict_mode')
12+
const REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for('react.profiler')
13+
const REACT_PROVIDER_TYPE = /* @__PURE__ */ Symbol.for('react.provider')
14+
const REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for('react.context')
15+
const REACT_SERVER_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for(
16+
'react.server_context',
17+
)
18+
const REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for('react.forward_ref')
19+
const REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for('react.suspense')
20+
const REACT_SUSPENSE_LIST_TYPE = /* @__PURE__ */ Symbol.for(
21+
'react.suspense_list',
22+
)
23+
const REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for('react.memo')
24+
const REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for('react.lazy')
25+
const REACT_OFFSCREEN_TYPE = /* @__PURE__ */ Symbol.for('react.offscreen')
26+
const REACT_CLIENT_REFERENCE = /* @__PURE__ */ Symbol.for(
27+
'react.client.reference',
28+
)
2329

2430
export const ForwardRef = REACT_FORWARD_REF_TYPE
2531
export const Memo = REACT_MEMO_TYPE

Diff for: src/utils/react.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import type * as ReactNamespace from 'react'
2-
import * as ReactOriginal from 'react'
1+
import * as React from 'react'
32

4-
export const React: typeof ReactNamespace =
5-
// prettier-ignore
6-
'default' in ReactOriginal ? ReactOriginal['default'] : ReactOriginal as any
3+
export { React }

Diff for: src/utils/useIsomorphicLayoutEffect.ts

+17-7
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,31 @@ import { React } from '../utils/react'
1010
// subscription is created and an inconsistent state may be observed
1111

1212
// Matches logic in React's `shared/ExecutionEnvironment` file
13-
export const canUseDOM = !!(
14-
typeof window !== 'undefined' &&
15-
typeof window.document !== 'undefined' &&
16-
typeof window.document.createElement !== 'undefined'
17-
)
13+
const canUseDOM = () =>
14+
!!(
15+
typeof window !== 'undefined' &&
16+
typeof window.document !== 'undefined' &&
17+
typeof window.document.createElement !== 'undefined'
18+
)
19+
20+
const isDOM = /* @__PURE__ */ canUseDOM()
1821

1922
// Under React Native, we know that we always want to use useLayoutEffect
2023

2124
/**
2225
* Checks if the code is running in a React Native environment.
2326
*
27+
* @returns Whether the code is running in a React Native environment.
28+
*
2429
* @see {@link https://github.com/facebook/react-native/issues/1331 Reference}
2530
*/
26-
export const isReactNative =
31+
const isRunningInReactNative = () =>
2732
typeof navigator !== 'undefined' && navigator.product === 'ReactNative'
2833

34+
const isReactNative = /* @__PURE__ */ isRunningInReactNative()
35+
36+
const getUseIsomorphicLayoutEffect = () =>
37+
isDOM || isReactNative ? React.useLayoutEffect : React.useEffect
38+
2939
export const useIsomorphicLayoutEffect =
30-
canUseDOM || isReactNative ? React.useLayoutEffect : React.useEffect
40+
/* @__PURE__ */ getUseIsomorphicLayoutEffect()

0 commit comments

Comments
 (0)