react-saga-redux 1.0.1

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.
Files changed (42) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +63 -0
  3. package/dist/index.d.mts +569 -0
  4. package/dist/index.d.ts +569 -0
  5. package/dist/index.js +1188 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +1138 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +100 -0
  10. package/src/components/Context.ts +50 -0
  11. package/src/components/Props.ts +111 -0
  12. package/src/components/Provider.tsx +105 -0
  13. package/src/components/connect.tsx +813 -0
  14. package/src/connect/invalidArgFactory.ts +14 -0
  15. package/src/connect/mapDispatchToProps.ts +25 -0
  16. package/src/connect/mapProps.ts +43 -0
  17. package/src/connect/mapStateToProps.ts +14 -0
  18. package/src/connect/mergeProps.ts +78 -0
  19. package/src/connect/selectorFactory.ts +242 -0
  20. package/src/connect/verifySubselectors.ts +26 -0
  21. package/src/connect/wrapMapToProps.ts +110 -0
  22. package/src/exports.ts +54 -0
  23. package/src/hooks/useDispatch.ts +104 -0
  24. package/src/hooks/useReduxContext.ts +42 -0
  25. package/src/hooks/useSelector.ts +286 -0
  26. package/src/hooks/useStore.ts +123 -0
  27. package/src/index-rsc.ts +34 -0
  28. package/src/index.ts +1 -0
  29. package/src/types.ts +180 -0
  30. package/src/utils/Subscription.ts +183 -0
  31. package/src/utils/batch.ts +4 -0
  32. package/src/utils/bindActionCreators.ts +16 -0
  33. package/src/utils/constants.ts +23 -0
  34. package/src/utils/hoistStatics.ts +139 -0
  35. package/src/utils/isPlainObject.ts +17 -0
  36. package/src/utils/react-is.ts +97 -0
  37. package/src/utils/react.ts +3 -0
  38. package/src/utils/shallowEqual.ts +36 -0
  39. package/src/utils/useIsomorphicLayoutEffect.ts +40 -0
  40. package/src/utils/useSyncExternalStore.ts +9 -0
  41. package/src/utils/verifyPlainObject.ts +14 -0
  42. package/src/utils/warning.ts +21 -0
@@ -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()
@@ -0,0 +1,42 @@
1
+ import { React } from '../utils/react'
2
+ import { ReactReduxContext } from '../components/Context'
3
+ import type { ReactReduxContextValue } from '../components/Context'
4
+
5
+ /**
6
+ * Hook factory, which creates a `useReduxContext` hook bound to a given context. This is a low-level
7
+ * hook that you should usually not need to call directly.
8
+ *
9
+ * @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
10
+ * @returns {Function} A `useReduxContext` hook bound to the specified context.
11
+ */
12
+ export function createReduxContextHook(context = ReactReduxContext) {
13
+ return function useReduxContext(): ReactReduxContextValue {
14
+ const contextValue = React.useContext(context)
15
+
16
+ if (process.env.NODE_ENV !== 'production' && !contextValue) {
17
+ throw new Error(
18
+ 'could not find react-redux context value; please ensure the component is wrapped in a <Provider>',
19
+ )
20
+ }
21
+
22
+ return contextValue!
23
+ }
24
+ }
25
+
26
+ /**
27
+ * A hook to access the value of the `ReactReduxContext`. This is a low-level
28
+ * hook that you should usually not need to call directly.
29
+ *
30
+ * @returns {any} the value of the `ReactReduxContext`
31
+ *
32
+ * @example
33
+ *
34
+ * import React from 'react'
35
+ * import { useReduxContext } from 'react-redux'
36
+ *
37
+ * export const CounterComponent = () => {
38
+ * const { store } = useReduxContext()
39
+ * return <div>{store.getState()}</div>
40
+ * }
41
+ */
42
+ export const useReduxContext = /*#__PURE__*/ createReduxContextHook()
@@ -0,0 +1,286 @@
1
+ //import * as React from 'react'
2
+ import { React } from '../utils/react'
3
+ import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'
4
+ import type { ReactReduxContextValue } from '../components/Context'
5
+ import { ReactReduxContext } from '../components/Context'
6
+ import type { EqualityFn, NoInfer } from '../types'
7
+ import {
8
+ createReduxContextHook,
9
+ useReduxContext as useDefaultReduxContext,
10
+ } from './useReduxContext'
11
+
12
+ /**
13
+ * The frequency of development mode checks.
14
+ *
15
+ * @since 8.1.0
16
+ * @internal
17
+ */
18
+ export type DevModeCheckFrequency = 'never' | 'once' | 'always'
19
+
20
+ /**
21
+ * Represents the configuration for development mode checks.
22
+ *
23
+ * @since 9.0.0
24
+ * @internal
25
+ */
26
+ export interface DevModeChecks {
27
+ /**
28
+ * Overrides the global stability check for the selector.
29
+ * - `once` - Run only the first time the selector is called.
30
+ * - `always` - Run every time the selector is called.
31
+ * - `never` - Never run the stability check.
32
+ *
33
+ * @default 'once'
34
+ *
35
+ * @since 8.1.0
36
+ */
37
+ stabilityCheck: DevModeCheckFrequency
38
+
39
+ /**
40
+ * Overrides the global identity function check for the selector.
41
+ * - `once` - Run only the first time the selector is called.
42
+ * - `always` - Run every time the selector is called.
43
+ * - `never` - Never run the identity function check.
44
+ *
45
+ * **Note**: Previously referred to as `noopCheck`.
46
+ *
47
+ * @default 'once'
48
+ *
49
+ * @since 9.0.0
50
+ */
51
+ identityFunctionCheck: DevModeCheckFrequency
52
+ }
53
+
54
+ export interface UseSelectorOptions<Selected = unknown> {
55
+ equalityFn?: EqualityFn<Selected>
56
+
57
+ /**
58
+ * `useSelector` performs additional checks in development mode to help
59
+ * identify and warn about potential issues in selector behavior. This
60
+ * option allows you to customize the behavior of these checks per selector.
61
+ *
62
+ * @since 9.0.0
63
+ */
64
+ devModeChecks?: Partial<DevModeChecks>
65
+ }
66
+
67
+ /**
68
+ * Represents a custom hook that allows you to extract data from the
69
+ * Redux store state, using a selector function. The selector function
70
+ * takes the current state as an argument and returns a part of the state
71
+ * or some derived data. The hook also supports an optional equality
72
+ * function or options object to customize its behavior.
73
+ *
74
+ * @template StateType - The specific type of state this hook operates on.
75
+ *
76
+ * @public
77
+ */
78
+ export interface UseSelector<StateType = unknown> {
79
+ /**
80
+ * A function that takes a selector function as its first argument.
81
+ * The selector function is responsible for selecting a part of
82
+ * the Redux store's state or computing derived data.
83
+ *
84
+ * @param selector - A function that receives the current state and returns a part of the state or some derived data.
85
+ * @param equalityFnOrOptions - An optional equality function or options object for customizing the behavior of the selector.
86
+ * @returns The selected part of the state or derived data.
87
+ *
88
+ * @template TState - The specific type of state this hook operates on.
89
+ * @template Selected - The type of the value that the selector function will return.
90
+ */
91
+ <TState extends StateType = StateType, Selected = unknown>(
92
+ selector: (state: TState) => Selected,
93
+ equalityFnOrOptions?: EqualityFn<Selected> | UseSelectorOptions<Selected>,
94
+ ): Selected
95
+
96
+ /**
97
+ * Creates a "pre-typed" version of {@linkcode useSelector useSelector}
98
+ * where the `state` type is predefined.
99
+ *
100
+ * This allows you to set the `state` type once, eliminating the need to
101
+ * specify it with every {@linkcode useSelector useSelector} call.
102
+ *
103
+ * @returns A pre-typed `useSelector` with the state type already defined.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * export const useAppSelector = useSelector.withTypes<RootState>()
108
+ * ```
109
+ *
110
+ * @template OverrideStateType - The specific type of state this hook operates on.
111
+ *
112
+ * @since 9.1.0
113
+ */
114
+ withTypes: <
115
+ OverrideStateType extends StateType,
116
+ >() => UseSelector<OverrideStateType>
117
+ }
118
+
119
+ const refEquality: EqualityFn<any> = (a, b) => a === b
120
+
121
+ /**
122
+ * Hook factory, which creates a `useSelector` hook bound to a given context.
123
+ *
124
+ * @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
125
+ * @returns {Function} A `useSelector` hook bound to the specified context.
126
+ */
127
+ export function createSelectorHook(
128
+ context: React.Context<ReactReduxContextValue<
129
+ any,
130
+ any
131
+ > | null> = ReactReduxContext,
132
+ ): UseSelector {
133
+ const useReduxContext =
134
+ context === ReactReduxContext
135
+ ? useDefaultReduxContext
136
+ : createReduxContextHook(context)
137
+
138
+ const useSelector = <TState, Selected>(
139
+ selector: (state: TState) => Selected,
140
+ equalityFnOrOptions:
141
+ | EqualityFn<NoInfer<Selected>>
142
+ | UseSelectorOptions<NoInfer<Selected>> = {},
143
+ ): Selected => {
144
+ const { equalityFn = refEquality } =
145
+ typeof equalityFnOrOptions === 'function'
146
+ ? { equalityFn: equalityFnOrOptions }
147
+ : equalityFnOrOptions
148
+ if (process.env.NODE_ENV !== 'production') {
149
+ if (!selector) {
150
+ throw new Error(`You must pass a selector to useSelector`)
151
+ }
152
+ if (typeof selector !== 'function') {
153
+ throw new Error(`You must pass a function as a selector to useSelector`)
154
+ }
155
+ if (typeof equalityFn !== 'function') {
156
+ throw new Error(
157
+ `You must pass a function as an equality function to useSelector`,
158
+ )
159
+ }
160
+ }
161
+
162
+ const reduxContext = useReduxContext()
163
+
164
+ const { store, subscription, getServerState } = reduxContext
165
+
166
+ const firstRun = React.useRef(true)
167
+
168
+ const wrappedSelector = React.useCallback<typeof selector>(
169
+ {
170
+ [selector.name](state: TState) {
171
+ const selected = selector(state)
172
+ if (process.env.NODE_ENV !== 'production') {
173
+ const { devModeChecks = {} } =
174
+ typeof equalityFnOrOptions === 'function'
175
+ ? {}
176
+ : equalityFnOrOptions
177
+ const { identityFunctionCheck, stabilityCheck } = reduxContext
178
+ const {
179
+ identityFunctionCheck: finalIdentityFunctionCheck,
180
+ stabilityCheck: finalStabilityCheck,
181
+ } = {
182
+ stabilityCheck,
183
+ identityFunctionCheck,
184
+ ...devModeChecks,
185
+ }
186
+ if (
187
+ finalStabilityCheck === 'always' ||
188
+ (finalStabilityCheck === 'once' && firstRun.current)
189
+ ) {
190
+ const toCompare = selector(state)
191
+ if (!equalityFn(selected, toCompare)) {
192
+ let stack: string | undefined = undefined
193
+ try {
194
+ throw new Error()
195
+ } catch (e) {
196
+ // eslint-disable-next-line no-extra-semi
197
+ ;({ stack } = e as Error)
198
+ }
199
+ console.warn(
200
+ 'Selector ' +
201
+ (selector.name || 'unknown') +
202
+ ' returned a different result when called with the same parameters. This can lead to unnecessary rerenders.' +
203
+ '\nSelectors that return a new reference (such as an object or an array) should be memoized: https://redux.js.org/usage/deriving-data-selectors#optimizing-selectors-with-memoization',
204
+ {
205
+ state,
206
+ selected,
207
+ selected2: toCompare,
208
+ stack,
209
+ },
210
+ )
211
+ }
212
+ }
213
+ if (
214
+ finalIdentityFunctionCheck === 'always' ||
215
+ (finalIdentityFunctionCheck === 'once' && firstRun.current)
216
+ ) {
217
+ // @ts-ignore
218
+ if (selected === state) {
219
+ let stack: string | undefined = undefined
220
+ try {
221
+ throw new Error()
222
+ } catch (e) {
223
+ // eslint-disable-next-line no-extra-semi
224
+ ;({ stack } = e as Error)
225
+ }
226
+ console.warn(
227
+ 'Selector ' +
228
+ (selector.name || 'unknown') +
229
+ ' returned the root state when called. This can lead to unnecessary rerenders.' +
230
+ '\nSelectors that return the entire state are almost certainly a mistake, as they will cause a rerender whenever *anything* in state changes.',
231
+ { stack },
232
+ )
233
+ }
234
+ }
235
+ if (firstRun.current) firstRun.current = false
236
+ }
237
+ return selected
238
+ },
239
+ }[selector.name],
240
+ [selector],
241
+ )
242
+
243
+ const selectedState = useSyncExternalStoreWithSelector(
244
+ subscription.addNestedSub,
245
+ store.getState,
246
+ getServerState || store.getState,
247
+ wrappedSelector,
248
+ equalityFn,
249
+ )
250
+
251
+ React.useDebugValue(selectedState)
252
+
253
+ return selectedState
254
+ }
255
+
256
+ Object.assign(useSelector, {
257
+ withTypes: () => useSelector,
258
+ })
259
+
260
+ return useSelector as UseSelector
261
+ }
262
+
263
+ /**
264
+ * A hook to access the redux store's state. This hook takes a selector function
265
+ * as an argument. The selector is called with the store state.
266
+ *
267
+ * This hook takes an optional equality comparison function as the second parameter
268
+ * that allows you to customize the way the selected state is compared to determine
269
+ * whether the component needs to be re-rendered.
270
+ *
271
+ * @param {Function} selector the selector function
272
+ * @param {Function=} equalityFn the function that will be used to determine equality
273
+ *
274
+ * @returns {any} the selected state
275
+ *
276
+ * @example
277
+ *
278
+ * import React from 'react'
279
+ * import { useSelector } from 'react-redux'
280
+ *
281
+ * export const CounterComponent = () => {
282
+ * const counter = useSelector(state => state.counter)
283
+ * return <div>{counter}</div>
284
+ * }
285
+ */
286
+ export const useSelector = /*#__PURE__*/ createSelectorHook()
@@ -0,0 +1,123 @@
1
+ import type { Context } from 'react'
2
+ import type { Action, Store } from 'redux'
3
+ import type { ReactReduxContextValue } from '../components/Context'
4
+ import { ReactReduxContext } from '../components/Context'
5
+ import {
6
+ createReduxContextHook,
7
+ useReduxContext as useDefaultReduxContext,
8
+ } from './useReduxContext'
9
+
10
+ /**
11
+ * Represents a type that extracts the action type from a given Redux store.
12
+ *
13
+ * @template StoreType - The specific type of the Redux store.
14
+ *
15
+ * @since 9.1.0
16
+ * @internal
17
+ */
18
+ export type ExtractStoreActionType<StoreType extends Store> =
19
+ StoreType extends Store<any, infer ActionType> ? ActionType : never
20
+
21
+ /**
22
+ * Represents a custom hook that provides access to the Redux store.
23
+ *
24
+ * @template StoreType - The specific type of the Redux store that gets returned.
25
+ *
26
+ * @since 9.1.0
27
+ * @public
28
+ */
29
+ export interface UseStore<StoreType extends Store> {
30
+ /**
31
+ * Returns the Redux store instance.
32
+ *
33
+ * @returns The Redux store instance.
34
+ */
35
+ (): StoreType
36
+
37
+ /**
38
+ * Returns the Redux store instance with specific state and action types.
39
+ *
40
+ * @returns The Redux store with the specified state and action types.
41
+ *
42
+ * @template StateType - The specific type of the state used in the store.
43
+ * @template ActionType - The specific type of the actions used in the store.
44
+ */
45
+ <
46
+ StateType extends ReturnType<StoreType['getState']> = ReturnType<
47
+ StoreType['getState']
48
+ >,
49
+ ActionType extends Action = ExtractStoreActionType<Store>,
50
+ >(): Store<StateType, ActionType>
51
+
52
+ /**
53
+ * Creates a "pre-typed" version of {@linkcode useStore useStore}
54
+ * where the type of the Redux `store` is predefined.
55
+ *
56
+ * This allows you to set the `store` type once, eliminating the need to
57
+ * specify it with every {@linkcode useStore useStore} call.
58
+ *
59
+ * @returns A pre-typed `useStore` with the store type already defined.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * export const useAppStore = useStore.withTypes<AppStore>()
64
+ * ```
65
+ *
66
+ * @template OverrideStoreType - The specific type of the Redux store that gets returned.
67
+ *
68
+ * @since 9.1.0
69
+ */
70
+ withTypes: <
71
+ OverrideStoreType extends StoreType,
72
+ >() => UseStore<OverrideStoreType>
73
+ }
74
+
75
+ /**
76
+ * Hook factory, which creates a `useStore` hook bound to a given context.
77
+ *
78
+ * @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
79
+ * @returns {Function} A `useStore` hook bound to the specified context.
80
+ */
81
+ export function createStoreHook<
82
+ StateType = unknown,
83
+ ActionType extends Action = Action,
84
+ >(
85
+ // @ts-ignore
86
+ context?: Context<ReactReduxContextValue<
87
+ StateType,
88
+ ActionType
89
+ > | null> = ReactReduxContext,
90
+ ) {
91
+ const useReduxContext =
92
+ context === ReactReduxContext
93
+ ? useDefaultReduxContext
94
+ : // @ts-ignore
95
+ createReduxContextHook(context)
96
+ const useStore = () => {
97
+ const { store } = useReduxContext()
98
+ return store
99
+ }
100
+
101
+ Object.assign(useStore, {
102
+ withTypes: () => useStore,
103
+ })
104
+
105
+ return useStore as UseStore<Store<StateType, ActionType>>
106
+ }
107
+
108
+ /**
109
+ * A hook to access the redux store.
110
+ *
111
+ * @returns {any} the redux store
112
+ *
113
+ * @example
114
+ *
115
+ * import React from 'react'
116
+ * import { useStore } from 'react-redux'
117
+ *
118
+ * export const ExampleComponent = () => {
119
+ * const store = useStore()
120
+ * return <div>{store.getState()}</div>
121
+ * }
122
+ */
123
+ export const useStore = /*#__PURE__*/ createStoreHook()
@@ -0,0 +1,34 @@
1
+ import type * as normal from './index'
2
+ import type * as rsc from './index-rsc'
3
+
4
+ // checks to make sure we didn't forgot to replicate any exports
5
+
6
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
7
+ const _check: typeof normal = {} as typeof rsc
8
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
9
+ const _check2: typeof rsc = {} as typeof normal
10
+
11
+ // -------------------------------------------------------------------------------------
12
+
13
+ const throwNotSupportedError = ((
14
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
+ ...args: any[]
16
+ ): any => {
17
+ throw new Error(
18
+ 'This function is not supported in React Server Components. Please only use this export in a Client Component.',
19
+ )
20
+ }) as any
21
+
22
+ export {
23
+ throwNotSupportedError as Provider,
24
+ throwNotSupportedError as batch,
25
+ throwNotSupportedError as connect,
26
+ throwNotSupportedError as createDispatchHook,
27
+ throwNotSupportedError as createSelectorHook,
28
+ throwNotSupportedError as createStoreHook,
29
+ throwNotSupportedError as useDispatch,
30
+ throwNotSupportedError as useSelector,
31
+ throwNotSupportedError as useStore,
32
+ }
33
+ export const ReactReduxContext = {} as any
34
+ export { default as shallowEqual } from './utils/shallowEqual'
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './exports'