@depup/react-redux 9.3.0-depup.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +32 -0
- package/changes.json +14 -0
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/react-redux.development.cjs +1119 -0
- package/dist/cjs/react-redux.development.cjs.map +1 -0
- package/dist/cjs/react-redux.production.min.cjs +6 -0
- package/dist/cjs/react-redux.production.min.cjs.map +1 -0
- package/dist/react-redux.browser.mjs +6 -0
- package/dist/react-redux.browser.mjs.map +1 -0
- package/dist/react-redux.d.ts +587 -0
- package/dist/react-redux.legacy-esm.js +1105 -0
- package/dist/react-redux.legacy-esm.js.map +1 -0
- package/dist/react-redux.mjs +1071 -0
- package/dist/react-redux.mjs.map +1 -0
- package/dist/rsc.mjs +46 -0
- package/dist/rsc.mjs.map +1 -0
- package/package.json +124 -0
- package/src/components/Context.ts +50 -0
- package/src/components/Provider.tsx +105 -0
- package/src/components/connect.tsx +845 -0
- package/src/connect/invalidArgFactory.ts +14 -0
- package/src/connect/mapDispatchToProps.ts +25 -0
- package/src/connect/mapStateToProps.ts +14 -0
- package/src/connect/mergeProps.ts +78 -0
- package/src/connect/selectorFactory.ts +242 -0
- package/src/connect/verifySubselectors.ts +26 -0
- package/src/connect/wrapMapToProps.ts +110 -0
- package/src/exports.ts +51 -0
- package/src/hooks/useDispatch.ts +104 -0
- package/src/hooks/useReduxContext.ts +42 -0
- package/src/hooks/useSelector.ts +286 -0
- package/src/hooks/useStore.ts +123 -0
- package/src/index-rsc.ts +35 -0
- package/src/index.ts +1 -0
- package/src/types.ts +180 -0
- package/src/utils/Subscription.ts +183 -0
- package/src/utils/batch.ts +4 -0
- package/src/utils/bindActionCreators.ts +16 -0
- package/src/utils/hoistStatics.ts +139 -0
- package/src/utils/isPlainObject.ts +17 -0
- package/src/utils/react-is.ts +97 -0
- package/src/utils/react.ts +3 -0
- package/src/utils/shallowEqual.ts +36 -0
- package/src/utils/useIsomorphicLayoutEffect.ts +40 -0
- package/src/utils/useSyncExternalStore.ts +9 -0
- package/src/utils/verifyPlainObject.ts +14 -0
- package/src/utils/warning.ts +21 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Action, Dispatch } from 'redux'
|
|
2
|
+
|
|
3
|
+
export function createInvalidArgFactory(arg: unknown, name: string) {
|
|
4
|
+
return (
|
|
5
|
+
dispatch: Dispatch<Action<string>>,
|
|
6
|
+
options: { readonly wrappedComponentName: string },
|
|
7
|
+
) => {
|
|
8
|
+
throw new Error(
|
|
9
|
+
`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${
|
|
10
|
+
options.wrappedComponentName
|
|
11
|
+
}.`,
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Action, Dispatch } from 'redux'
|
|
2
|
+
import bindActionCreators from '../utils/bindActionCreators'
|
|
3
|
+
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'
|
|
4
|
+
import { createInvalidArgFactory } from './invalidArgFactory'
|
|
5
|
+
import type { MapDispatchToPropsParam } from './selectorFactory'
|
|
6
|
+
|
|
7
|
+
export function mapDispatchToPropsFactory<TDispatchProps, TOwnProps>(
|
|
8
|
+
mapDispatchToProps:
|
|
9
|
+
| MapDispatchToPropsParam<TDispatchProps, TOwnProps>
|
|
10
|
+
| undefined,
|
|
11
|
+
) {
|
|
12
|
+
return mapDispatchToProps && typeof mapDispatchToProps === 'object'
|
|
13
|
+
? wrapMapToPropsConstant((dispatch: Dispatch<Action<string>>) =>
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
bindActionCreators(mapDispatchToProps, dispatch),
|
|
16
|
+
)
|
|
17
|
+
: !mapDispatchToProps
|
|
18
|
+
? wrapMapToPropsConstant((dispatch: Dispatch<Action<string>>) => ({
|
|
19
|
+
dispatch,
|
|
20
|
+
}))
|
|
21
|
+
: typeof mapDispatchToProps === 'function'
|
|
22
|
+
? // @ts-ignore
|
|
23
|
+
wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps')
|
|
24
|
+
: createInvalidArgFactory(mapDispatchToProps, 'mapDispatchToProps')
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'
|
|
2
|
+
import { createInvalidArgFactory } from './invalidArgFactory'
|
|
3
|
+
import type { MapStateToPropsParam } from './selectorFactory'
|
|
4
|
+
|
|
5
|
+
export function mapStateToPropsFactory<TStateProps, TOwnProps, State>(
|
|
6
|
+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
|
|
7
|
+
) {
|
|
8
|
+
return !mapStateToProps
|
|
9
|
+
? wrapMapToPropsConstant(() => ({}))
|
|
10
|
+
: typeof mapStateToProps === 'function'
|
|
11
|
+
? // @ts-ignore
|
|
12
|
+
wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
|
|
13
|
+
: createInvalidArgFactory(mapStateToProps, 'mapStateToProps')
|
|
14
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Action, Dispatch } from 'redux'
|
|
2
|
+
import verifyPlainObject from '../utils/verifyPlainObject'
|
|
3
|
+
import { createInvalidArgFactory } from './invalidArgFactory'
|
|
4
|
+
import type { MergeProps } from './selectorFactory'
|
|
5
|
+
import type { EqualityFn } from '../types'
|
|
6
|
+
|
|
7
|
+
function defaultMergeProps<
|
|
8
|
+
TStateProps,
|
|
9
|
+
TDispatchProps,
|
|
10
|
+
TOwnProps,
|
|
11
|
+
TMergedProps,
|
|
12
|
+
>(
|
|
13
|
+
stateProps: TStateProps,
|
|
14
|
+
dispatchProps: TDispatchProps,
|
|
15
|
+
ownProps: TOwnProps,
|
|
16
|
+
): TMergedProps {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
return { ...ownProps, ...stateProps, ...dispatchProps }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function wrapMergePropsFunc<
|
|
22
|
+
TStateProps,
|
|
23
|
+
TDispatchProps,
|
|
24
|
+
TOwnProps,
|
|
25
|
+
TMergedProps,
|
|
26
|
+
>(
|
|
27
|
+
mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
|
|
28
|
+
): (
|
|
29
|
+
dispatch: Dispatch<Action<string>>,
|
|
30
|
+
options: {
|
|
31
|
+
readonly displayName: string
|
|
32
|
+
readonly areMergedPropsEqual: EqualityFn<TMergedProps>
|
|
33
|
+
},
|
|
34
|
+
) => MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps> {
|
|
35
|
+
return function initMergePropsProxy(
|
|
36
|
+
dispatch,
|
|
37
|
+
{ displayName, areMergedPropsEqual },
|
|
38
|
+
) {
|
|
39
|
+
let hasRunOnce = false
|
|
40
|
+
let mergedProps: TMergedProps
|
|
41
|
+
|
|
42
|
+
return function mergePropsProxy(
|
|
43
|
+
stateProps: TStateProps,
|
|
44
|
+
dispatchProps: TDispatchProps,
|
|
45
|
+
ownProps: TOwnProps,
|
|
46
|
+
) {
|
|
47
|
+
const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps)
|
|
48
|
+
|
|
49
|
+
if (hasRunOnce) {
|
|
50
|
+
if (!areMergedPropsEqual(nextMergedProps, mergedProps))
|
|
51
|
+
mergedProps = nextMergedProps
|
|
52
|
+
} else {
|
|
53
|
+
hasRunOnce = true
|
|
54
|
+
mergedProps = nextMergedProps
|
|
55
|
+
|
|
56
|
+
if (process.env.NODE_ENV !== 'production')
|
|
57
|
+
verifyPlainObject(mergedProps, displayName, 'mergeProps')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return mergedProps
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function mergePropsFactory<
|
|
66
|
+
TStateProps,
|
|
67
|
+
TDispatchProps,
|
|
68
|
+
TOwnProps,
|
|
69
|
+
TMergedProps,
|
|
70
|
+
>(
|
|
71
|
+
mergeProps?: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
|
|
72
|
+
) {
|
|
73
|
+
return !mergeProps
|
|
74
|
+
? () => defaultMergeProps
|
|
75
|
+
: typeof mergeProps === 'function'
|
|
76
|
+
? wrapMergePropsFunc(mergeProps)
|
|
77
|
+
: createInvalidArgFactory(mergeProps, 'mergeProps')
|
|
78
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type { Dispatch, Action } from 'redux'
|
|
2
|
+
import type { ComponentType } from 'react'
|
|
3
|
+
import verifySubselectors from './verifySubselectors'
|
|
4
|
+
import type { EqualityFn, ExtendedEqualityFn } from '../types'
|
|
5
|
+
|
|
6
|
+
export type SelectorFactory<S, TProps, TOwnProps, TFactoryOptions> = (
|
|
7
|
+
dispatch: Dispatch<Action<string>>,
|
|
8
|
+
factoryOptions: TFactoryOptions,
|
|
9
|
+
) => Selector<S, TProps, TOwnProps>
|
|
10
|
+
|
|
11
|
+
export type Selector<S, TProps, TOwnProps = null> = TOwnProps extends
|
|
12
|
+
| null
|
|
13
|
+
| undefined
|
|
14
|
+
? (state: S) => TProps
|
|
15
|
+
: (state: S, ownProps: TOwnProps) => TProps
|
|
16
|
+
|
|
17
|
+
export type MapStateToProps<TStateProps, TOwnProps, State> = (
|
|
18
|
+
state: State,
|
|
19
|
+
ownProps: TOwnProps,
|
|
20
|
+
) => TStateProps
|
|
21
|
+
|
|
22
|
+
export type MapStateToPropsFactory<TStateProps, TOwnProps, State> = (
|
|
23
|
+
initialState: State,
|
|
24
|
+
ownProps: TOwnProps,
|
|
25
|
+
) => MapStateToProps<TStateProps, TOwnProps, State>
|
|
26
|
+
|
|
27
|
+
export type MapStateToPropsParam<TStateProps, TOwnProps, State> =
|
|
28
|
+
| MapStateToPropsFactory<TStateProps, TOwnProps, State>
|
|
29
|
+
| MapStateToProps<TStateProps, TOwnProps, State>
|
|
30
|
+
| null
|
|
31
|
+
| undefined
|
|
32
|
+
|
|
33
|
+
export type MapDispatchToPropsFunction<TDispatchProps, TOwnProps> = (
|
|
34
|
+
dispatch: Dispatch<Action<string>>,
|
|
35
|
+
ownProps: TOwnProps,
|
|
36
|
+
) => TDispatchProps
|
|
37
|
+
|
|
38
|
+
export type MapDispatchToProps<TDispatchProps, TOwnProps> =
|
|
39
|
+
| MapDispatchToPropsFunction<TDispatchProps, TOwnProps>
|
|
40
|
+
| TDispatchProps
|
|
41
|
+
|
|
42
|
+
export type MapDispatchToPropsFactory<TDispatchProps, TOwnProps> = (
|
|
43
|
+
dispatch: Dispatch<Action<string>>,
|
|
44
|
+
ownProps: TOwnProps,
|
|
45
|
+
) => MapDispatchToPropsFunction<TDispatchProps, TOwnProps>
|
|
46
|
+
|
|
47
|
+
export type MapDispatchToPropsParam<TDispatchProps, TOwnProps> =
|
|
48
|
+
| MapDispatchToPropsFactory<TDispatchProps, TOwnProps>
|
|
49
|
+
| MapDispatchToProps<TDispatchProps, TOwnProps>
|
|
50
|
+
|
|
51
|
+
export type MapDispatchToPropsNonObject<TDispatchProps, TOwnProps> =
|
|
52
|
+
| MapDispatchToPropsFactory<TDispatchProps, TOwnProps>
|
|
53
|
+
| MapDispatchToPropsFunction<TDispatchProps, TOwnProps>
|
|
54
|
+
|
|
55
|
+
export type MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps> = (
|
|
56
|
+
stateProps: TStateProps,
|
|
57
|
+
dispatchProps: TDispatchProps,
|
|
58
|
+
ownProps: TOwnProps,
|
|
59
|
+
) => TMergedProps
|
|
60
|
+
|
|
61
|
+
interface PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State> {
|
|
62
|
+
readonly areStatesEqual: ExtendedEqualityFn<State, TOwnProps>
|
|
63
|
+
readonly areStatePropsEqual: EqualityFn<TStateProps>
|
|
64
|
+
readonly areOwnPropsEqual: EqualityFn<TOwnProps>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function pureFinalPropsSelectorFactory<
|
|
68
|
+
TStateProps,
|
|
69
|
+
TOwnProps,
|
|
70
|
+
TDispatchProps,
|
|
71
|
+
TMergedProps,
|
|
72
|
+
State,
|
|
73
|
+
>(
|
|
74
|
+
mapStateToProps: WrappedMapStateToProps<TStateProps, TOwnProps, State>,
|
|
75
|
+
mapDispatchToProps: WrappedMapDispatchToProps<TDispatchProps, TOwnProps>,
|
|
76
|
+
mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
|
|
77
|
+
dispatch: Dispatch<Action<string>>,
|
|
78
|
+
{
|
|
79
|
+
areStatesEqual,
|
|
80
|
+
areOwnPropsEqual,
|
|
81
|
+
areStatePropsEqual,
|
|
82
|
+
}: PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State>,
|
|
83
|
+
) {
|
|
84
|
+
let hasRunAtLeastOnce = false
|
|
85
|
+
let state: State
|
|
86
|
+
let ownProps: TOwnProps
|
|
87
|
+
let stateProps: TStateProps
|
|
88
|
+
let dispatchProps: TDispatchProps
|
|
89
|
+
let mergedProps: TMergedProps
|
|
90
|
+
|
|
91
|
+
function handleFirstCall(firstState: State, firstOwnProps: TOwnProps) {
|
|
92
|
+
state = firstState
|
|
93
|
+
ownProps = firstOwnProps
|
|
94
|
+
stateProps = mapStateToProps(state, ownProps)
|
|
95
|
+
dispatchProps = mapDispatchToProps(dispatch, ownProps)
|
|
96
|
+
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
|
|
97
|
+
hasRunAtLeastOnce = true
|
|
98
|
+
return mergedProps
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function handleNewPropsAndNewState() {
|
|
102
|
+
stateProps = mapStateToProps(state, ownProps)
|
|
103
|
+
|
|
104
|
+
if (mapDispatchToProps.dependsOnOwnProps)
|
|
105
|
+
dispatchProps = mapDispatchToProps(dispatch, ownProps)
|
|
106
|
+
|
|
107
|
+
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
|
|
108
|
+
return mergedProps
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function handleNewProps() {
|
|
112
|
+
if (mapStateToProps.dependsOnOwnProps)
|
|
113
|
+
stateProps = mapStateToProps(state, ownProps)
|
|
114
|
+
|
|
115
|
+
if (mapDispatchToProps.dependsOnOwnProps)
|
|
116
|
+
dispatchProps = mapDispatchToProps(dispatch, ownProps)
|
|
117
|
+
|
|
118
|
+
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
|
|
119
|
+
return mergedProps
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function handleNewState() {
|
|
123
|
+
const nextStateProps = mapStateToProps(state, ownProps)
|
|
124
|
+
const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
|
|
125
|
+
stateProps = nextStateProps
|
|
126
|
+
|
|
127
|
+
if (statePropsChanged)
|
|
128
|
+
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
|
|
129
|
+
|
|
130
|
+
return mergedProps
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function handleSubsequentCalls(nextState: State, nextOwnProps: TOwnProps) {
|
|
134
|
+
const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
|
|
135
|
+
const stateChanged = !areStatesEqual(
|
|
136
|
+
nextState,
|
|
137
|
+
state,
|
|
138
|
+
nextOwnProps,
|
|
139
|
+
ownProps,
|
|
140
|
+
)
|
|
141
|
+
state = nextState
|
|
142
|
+
ownProps = nextOwnProps
|
|
143
|
+
|
|
144
|
+
if (propsChanged && stateChanged) return handleNewPropsAndNewState()
|
|
145
|
+
if (propsChanged) return handleNewProps()
|
|
146
|
+
if (stateChanged) return handleNewState()
|
|
147
|
+
return mergedProps
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return function pureFinalPropsSelector(
|
|
151
|
+
nextState: State,
|
|
152
|
+
nextOwnProps: TOwnProps,
|
|
153
|
+
) {
|
|
154
|
+
return hasRunAtLeastOnce
|
|
155
|
+
? handleSubsequentCalls(nextState, nextOwnProps)
|
|
156
|
+
: handleFirstCall(nextState, nextOwnProps)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface WrappedMapStateToProps<TStateProps, TOwnProps, State> {
|
|
161
|
+
(state: State, ownProps: TOwnProps): TStateProps
|
|
162
|
+
readonly dependsOnOwnProps: boolean
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
interface WrappedMapDispatchToProps<TDispatchProps, TOwnProps> {
|
|
166
|
+
(dispatch: Dispatch<Action<string>>, ownProps: TOwnProps): TDispatchProps
|
|
167
|
+
readonly dependsOnOwnProps: boolean
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface InitOptions<TStateProps, TOwnProps, TMergedProps, State>
|
|
171
|
+
extends PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State> {
|
|
172
|
+
readonly shouldHandleStateChanges: boolean
|
|
173
|
+
readonly displayName: string
|
|
174
|
+
readonly wrappedComponentName: string
|
|
175
|
+
readonly WrappedComponent: ComponentType<TOwnProps>
|
|
176
|
+
readonly areMergedPropsEqual: EqualityFn<TMergedProps>
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface SelectorFactoryOptions<
|
|
180
|
+
TStateProps,
|
|
181
|
+
TOwnProps,
|
|
182
|
+
TDispatchProps,
|
|
183
|
+
TMergedProps,
|
|
184
|
+
State,
|
|
185
|
+
> extends InitOptions<TStateProps, TOwnProps, TMergedProps, State> {
|
|
186
|
+
readonly initMapStateToProps: (
|
|
187
|
+
dispatch: Dispatch<Action<string>>,
|
|
188
|
+
options: InitOptions<TStateProps, TOwnProps, TMergedProps, State>,
|
|
189
|
+
) => WrappedMapStateToProps<TStateProps, TOwnProps, State>
|
|
190
|
+
readonly initMapDispatchToProps: (
|
|
191
|
+
dispatch: Dispatch<Action<string>>,
|
|
192
|
+
options: InitOptions<TStateProps, TOwnProps, TMergedProps, State>,
|
|
193
|
+
) => WrappedMapDispatchToProps<TDispatchProps, TOwnProps>
|
|
194
|
+
readonly initMergeProps: (
|
|
195
|
+
dispatch: Dispatch<Action<string>>,
|
|
196
|
+
options: InitOptions<TStateProps, TOwnProps, TMergedProps, State>,
|
|
197
|
+
) => MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// TODO: Add more comments
|
|
201
|
+
|
|
202
|
+
// The selector returned by selectorFactory will memoize its results,
|
|
203
|
+
// allowing connect's shouldComponentUpdate to return false if final
|
|
204
|
+
// props have not changed.
|
|
205
|
+
|
|
206
|
+
export default function finalPropsSelectorFactory<
|
|
207
|
+
TStateProps,
|
|
208
|
+
TOwnProps,
|
|
209
|
+
TDispatchProps,
|
|
210
|
+
TMergedProps,
|
|
211
|
+
State,
|
|
212
|
+
>(
|
|
213
|
+
dispatch: Dispatch<Action<string>>,
|
|
214
|
+
{
|
|
215
|
+
initMapStateToProps,
|
|
216
|
+
initMapDispatchToProps,
|
|
217
|
+
initMergeProps,
|
|
218
|
+
...options
|
|
219
|
+
}: SelectorFactoryOptions<
|
|
220
|
+
TStateProps,
|
|
221
|
+
TOwnProps,
|
|
222
|
+
TDispatchProps,
|
|
223
|
+
TMergedProps,
|
|
224
|
+
State
|
|
225
|
+
>,
|
|
226
|
+
) {
|
|
227
|
+
const mapStateToProps = initMapStateToProps(dispatch, options)
|
|
228
|
+
const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
|
|
229
|
+
const mergeProps = initMergeProps(dispatch, options)
|
|
230
|
+
|
|
231
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
232
|
+
verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return pureFinalPropsSelectorFactory<
|
|
236
|
+
TStateProps,
|
|
237
|
+
TOwnProps,
|
|
238
|
+
TDispatchProps,
|
|
239
|
+
TMergedProps,
|
|
240
|
+
State
|
|
241
|
+
>(mapStateToProps, mapDispatchToProps, mergeProps, dispatch, options)
|
|
242
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import warning from '../utils/warning'
|
|
2
|
+
|
|
3
|
+
function verify(selector: unknown, methodName: string): void {
|
|
4
|
+
if (!selector) {
|
|
5
|
+
throw new Error(`Unexpected value for ${methodName} in connect.`)
|
|
6
|
+
} else if (
|
|
7
|
+
methodName === 'mapStateToProps' ||
|
|
8
|
+
methodName === 'mapDispatchToProps'
|
|
9
|
+
) {
|
|
10
|
+
if (!Object.prototype.hasOwnProperty.call(selector, 'dependsOnOwnProps')) {
|
|
11
|
+
warning(
|
|
12
|
+
`The selector for ${methodName} of connect did not specify a value for dependsOnOwnProps.`,
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function verifySubselectors(
|
|
19
|
+
mapStateToProps: unknown,
|
|
20
|
+
mapDispatchToProps: unknown,
|
|
21
|
+
mergeProps: unknown,
|
|
22
|
+
): void {
|
|
23
|
+
verify(mapStateToProps, 'mapStateToProps')
|
|
24
|
+
verify(mapDispatchToProps, 'mapDispatchToProps')
|
|
25
|
+
verify(mergeProps, 'mergeProps')
|
|
26
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { ActionCreatorsMapObject, Dispatch, ActionCreator } from 'redux'
|
|
2
|
+
|
|
3
|
+
import type { FixTypeLater } from '../types'
|
|
4
|
+
import verifyPlainObject from '../utils/verifyPlainObject'
|
|
5
|
+
|
|
6
|
+
type AnyState = { [key: string]: any }
|
|
7
|
+
type StateOrDispatch<S extends AnyState = AnyState> = S | Dispatch
|
|
8
|
+
|
|
9
|
+
type AnyProps = { [key: string]: any }
|
|
10
|
+
|
|
11
|
+
export type MapToProps<P extends AnyProps = AnyProps> = {
|
|
12
|
+
// eslint-disable-next-line no-unused-vars
|
|
13
|
+
(stateOrDispatch: StateOrDispatch, ownProps?: P): FixTypeLater
|
|
14
|
+
dependsOnOwnProps?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function wrapMapToPropsConstant(
|
|
18
|
+
// * Note:
|
|
19
|
+
// It seems that the dispatch argument
|
|
20
|
+
// could be a dispatch function in some cases (ex: whenMapDispatchToPropsIsMissing)
|
|
21
|
+
// and a state object in some others (ex: whenMapStateToPropsIsMissing)
|
|
22
|
+
// eslint-disable-next-line no-unused-vars
|
|
23
|
+
getConstant: (dispatch: Dispatch) =>
|
|
24
|
+
| {
|
|
25
|
+
dispatch?: Dispatch
|
|
26
|
+
dependsOnOwnProps?: boolean
|
|
27
|
+
}
|
|
28
|
+
| ActionCreatorsMapObject
|
|
29
|
+
| ActionCreator<any>,
|
|
30
|
+
) {
|
|
31
|
+
return function initConstantSelector(dispatch: Dispatch) {
|
|
32
|
+
const constant = getConstant(dispatch)
|
|
33
|
+
|
|
34
|
+
function constantSelector() {
|
|
35
|
+
return constant
|
|
36
|
+
}
|
|
37
|
+
constantSelector.dependsOnOwnProps = false
|
|
38
|
+
return constantSelector
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// dependsOnOwnProps is used by createMapToPropsProxy to determine whether to pass props as args
|
|
43
|
+
// to the mapToProps function being wrapped. It is also used by makePurePropsSelector to determine
|
|
44
|
+
// whether mapToProps needs to be invoked when props have changed.
|
|
45
|
+
//
|
|
46
|
+
// A length of one signals that mapToProps does not depend on props from the parent component.
|
|
47
|
+
// A length of zero is assumed to mean mapToProps is getting args via arguments or ...args and
|
|
48
|
+
// therefore not reporting its length accurately..
|
|
49
|
+
// TODO Can this get pulled out so that we can subscribe directly to the store if we don't need ownProps?
|
|
50
|
+
function getDependsOnOwnProps(mapToProps: MapToProps) {
|
|
51
|
+
return mapToProps.dependsOnOwnProps
|
|
52
|
+
? Boolean(mapToProps.dependsOnOwnProps)
|
|
53
|
+
: mapToProps.length !== 1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
|
|
57
|
+
// this function wraps mapToProps in a proxy function which does several things:
|
|
58
|
+
//
|
|
59
|
+
// * Detects whether the mapToProps function being called depends on props, which
|
|
60
|
+
// is used by selectorFactory to decide if it should reinvoke on props changes.
|
|
61
|
+
//
|
|
62
|
+
// * On first call, handles mapToProps if returns another function, and treats that
|
|
63
|
+
// new function as the true mapToProps for subsequent calls.
|
|
64
|
+
//
|
|
65
|
+
// * On first call, verifies the first result is a plain object, in order to warn
|
|
66
|
+
// the developer that their mapToProps function is not returning a valid result.
|
|
67
|
+
//
|
|
68
|
+
export function wrapMapToPropsFunc<P extends AnyProps = AnyProps>(
|
|
69
|
+
mapToProps: MapToProps,
|
|
70
|
+
methodName: string,
|
|
71
|
+
) {
|
|
72
|
+
return function initProxySelector(
|
|
73
|
+
dispatch: Dispatch,
|
|
74
|
+
{ displayName }: { displayName: string },
|
|
75
|
+
) {
|
|
76
|
+
const proxy = function mapToPropsProxy(
|
|
77
|
+
stateOrDispatch: StateOrDispatch,
|
|
78
|
+
ownProps?: P,
|
|
79
|
+
): MapToProps {
|
|
80
|
+
return proxy.dependsOnOwnProps
|
|
81
|
+
? proxy.mapToProps(stateOrDispatch, ownProps)
|
|
82
|
+
: proxy.mapToProps(stateOrDispatch, undefined)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// allow detectFactoryAndVerify to get ownProps
|
|
86
|
+
proxy.dependsOnOwnProps = true
|
|
87
|
+
|
|
88
|
+
proxy.mapToProps = function detectFactoryAndVerify(
|
|
89
|
+
stateOrDispatch: StateOrDispatch,
|
|
90
|
+
ownProps?: P,
|
|
91
|
+
): MapToProps {
|
|
92
|
+
proxy.mapToProps = mapToProps
|
|
93
|
+
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
|
|
94
|
+
let props = proxy(stateOrDispatch, ownProps)
|
|
95
|
+
|
|
96
|
+
if (typeof props === 'function') {
|
|
97
|
+
proxy.mapToProps = props
|
|
98
|
+
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
|
|
99
|
+
props = proxy(stateOrDispatch, ownProps)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (process.env.NODE_ENV !== 'production')
|
|
103
|
+
verifyPlainObject(props, displayName, methodName)
|
|
104
|
+
|
|
105
|
+
return props
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return proxy
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/exports.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { connect, legacy_connect } from './components/connect'
|
|
2
|
+
export type {
|
|
3
|
+
Connect,
|
|
4
|
+
ConnectProps,
|
|
5
|
+
ConnectedProps,
|
|
6
|
+
} from './components/connect'
|
|
7
|
+
|
|
8
|
+
import shallowEqual from './utils/shallowEqual'
|
|
9
|
+
|
|
10
|
+
import Provider from './components/Provider'
|
|
11
|
+
import { defaultNoopBatch } from './utils/batch'
|
|
12
|
+
|
|
13
|
+
export { ReactReduxContext } from './components/Context'
|
|
14
|
+
export type { ReactReduxContextValue } from './components/Context'
|
|
15
|
+
|
|
16
|
+
export type { ProviderProps } from './components/Provider'
|
|
17
|
+
|
|
18
|
+
export type {
|
|
19
|
+
MapDispatchToProps,
|
|
20
|
+
MapDispatchToPropsFactory,
|
|
21
|
+
MapDispatchToPropsFunction,
|
|
22
|
+
MapDispatchToPropsNonObject,
|
|
23
|
+
MapDispatchToPropsParam,
|
|
24
|
+
MapStateToProps,
|
|
25
|
+
MapStateToPropsFactory,
|
|
26
|
+
MapStateToPropsParam,
|
|
27
|
+
MergeProps,
|
|
28
|
+
Selector,
|
|
29
|
+
SelectorFactory,
|
|
30
|
+
} from './connect/selectorFactory'
|
|
31
|
+
|
|
32
|
+
export { createDispatchHook, useDispatch } from './hooks/useDispatch'
|
|
33
|
+
export type { UseDispatch } from './hooks/useDispatch'
|
|
34
|
+
|
|
35
|
+
export { createSelectorHook, useSelector } from './hooks/useSelector'
|
|
36
|
+
export type { UseSelector } from './hooks/useSelector'
|
|
37
|
+
|
|
38
|
+
export { createStoreHook, useStore } from './hooks/useStore'
|
|
39
|
+
export type { UseStore } from './hooks/useStore'
|
|
40
|
+
|
|
41
|
+
export type { Subscription } from './utils/Subscription'
|
|
42
|
+
|
|
43
|
+
export * from './types'
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated As of React 18, batching is enabled by default for ReactDOM and React Native.
|
|
47
|
+
* This is now a no-op that immediately runs the callback.
|
|
48
|
+
*/
|
|
49
|
+
const batch = defaultNoopBatch
|
|
50
|
+
|
|
51
|
+
export { Provider, batch, connect, legacy_connect, shallowEqual }
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Context } from 'react'
|
|
2
|
+
import type { Action, Dispatch, UnknownAction } from 'redux'
|
|
3
|
+
|
|
4
|
+
import type { ReactReduxContextValue } from '../components/Context'
|
|
5
|
+
import { ReactReduxContext } from '../components/Context'
|
|
6
|
+
import { createStoreHook, useStore as useDefaultStore } from './useStore'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Represents a custom hook that provides a dispatch function
|
|
10
|
+
* from the Redux store.
|
|
11
|
+
*
|
|
12
|
+
* @template DispatchType - The specific type of the dispatch function.
|
|
13
|
+
*
|
|
14
|
+
* @since 9.1.0
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export interface UseDispatch<
|
|
18
|
+
DispatchType extends Dispatch<UnknownAction> = Dispatch<UnknownAction>,
|
|
19
|
+
> {
|
|
20
|
+
/**
|
|
21
|
+
* Returns the dispatch function from the Redux store.
|
|
22
|
+
*
|
|
23
|
+
* @returns The dispatch function from the Redux store.
|
|
24
|
+
*
|
|
25
|
+
* @template AppDispatch - The specific type of the dispatch function.
|
|
26
|
+
*/
|
|
27
|
+
<AppDispatch extends DispatchType = DispatchType>(): AppDispatch
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Creates a "pre-typed" version of {@linkcode useDispatch useDispatch}
|
|
31
|
+
* where the type of the `dispatch` function is predefined.
|
|
32
|
+
*
|
|
33
|
+
* This allows you to set the `dispatch` type once, eliminating the need to
|
|
34
|
+
* specify it with every {@linkcode useDispatch useDispatch} call.
|
|
35
|
+
*
|
|
36
|
+
* @returns A pre-typed `useDispatch` with the dispatch type already defined.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @template OverrideDispatchType - The specific type of the dispatch function.
|
|
44
|
+
*
|
|
45
|
+
* @since 9.1.0
|
|
46
|
+
*/
|
|
47
|
+
withTypes: <
|
|
48
|
+
OverrideDispatchType extends DispatchType,
|
|
49
|
+
>() => UseDispatch<OverrideDispatchType>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Hook factory, which creates a `useDispatch` hook bound to a given context.
|
|
54
|
+
*
|
|
55
|
+
* @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
|
|
56
|
+
* @returns {Function} A `useDispatch` hook bound to the specified context.
|
|
57
|
+
*/
|
|
58
|
+
export function createDispatchHook<
|
|
59
|
+
StateType = unknown,
|
|
60
|
+
ActionType extends Action = UnknownAction,
|
|
61
|
+
>(
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
context?: Context<ReactReduxContextValue<
|
|
64
|
+
StateType,
|
|
65
|
+
ActionType
|
|
66
|
+
> | null> = ReactReduxContext,
|
|
67
|
+
) {
|
|
68
|
+
const useStore =
|
|
69
|
+
context === ReactReduxContext ? useDefaultStore : createStoreHook(context)
|
|
70
|
+
|
|
71
|
+
const useDispatch = () => {
|
|
72
|
+
const store = useStore()
|
|
73
|
+
return store.dispatch
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Object.assign(useDispatch, {
|
|
77
|
+
withTypes: () => useDispatch,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
return useDispatch as UseDispatch<Dispatch<ActionType>>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A hook to access the redux `dispatch` function.
|
|
85
|
+
*
|
|
86
|
+
* @returns {any|function} redux store's `dispatch` function
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
*
|
|
90
|
+
* import React, { useCallback } from 'react'
|
|
91
|
+
* import { useDispatch } from 'react-redux'
|
|
92
|
+
*
|
|
93
|
+
* export const CounterComponent = ({ value }) => {
|
|
94
|
+
* const dispatch = useDispatch()
|
|
95
|
+
* const increaseCounter = useCallback(() => dispatch({ type: 'increase-counter' }), [])
|
|
96
|
+
* return (
|
|
97
|
+
* <div>
|
|
98
|
+
* <span>{value}</span>
|
|
99
|
+
* <button onClick={increaseCounter}>Increase counter</button>
|
|
100
|
+
* </div>
|
|
101
|
+
* )
|
|
102
|
+
* }
|
|
103
|
+
*/
|
|
104
|
+
export const useDispatch = /*#__PURE__*/ createDispatchHook()
|