/
configureStore.ts
196 lines (173 loc) · 6.26 KB
/
configureStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import type {
Reducer,
ReducersMapObject,
Middleware,
Action,
AnyAction,
StoreEnhancer,
Store,
Dispatch,
PreloadedState,
CombinedState,
} from 'redux'
import { createStore, compose, applyMiddleware, combineReducers } from 'redux'
import type { DevToolsEnhancerOptions as DevToolsOptions } from './devtoolsExtension'
import { composeWithDevTools } from './devtoolsExtension'
import isPlainObject from './isPlainObject'
import type {
ThunkMiddlewareFor,
CurriedGetDefaultMiddleware,
} from './getDefaultMiddleware'
import { curryGetDefaultMiddleware } from './getDefaultMiddleware'
import type { NoInfer, ExtractDispatchExtensions } from './tsHelpers'
const IS_PRODUCTION = process.env.NODE_ENV === 'production'
/**
* Callback function type, to be used in `ConfigureStoreOptions.enhancers`
*
* @public
*/
export type ConfigureEnhancersCallback = (
defaultEnhancers: readonly StoreEnhancer[]
) => StoreEnhancer[]
/**
* Options for `configureStore()`.
*
* @public
*/
export interface ConfigureStoreOptions<
S = any,
A extends Action = AnyAction,
M extends Middlewares<S> = Middlewares<S>
> {
/**
* A single reducer function that will be used as the root reducer, or an
* object of slice reducers that will be passed to `combineReducers()`.
*/
reducer: Reducer<S, A> | ReducersMapObject<S, A>
/**
* An array of Redux middleware to install. If not supplied, defaults to
* the set of middleware returned by `getDefaultMiddleware()`.
*
* @example `middleware: (gDM) => gDM().concat(logger, apiMiddleware, yourCustomMiddleware)`
* @see https://redux-toolkit.js.org/api/getDefaultMiddleware#intended-usage
*/
middleware?: ((getDefaultMiddleware: CurriedGetDefaultMiddleware<S>) => M) | M
/**
* Whether to enable Redux DevTools integration. Defaults to `true`.
*
* Additional configuration can be done by passing Redux DevTools options
*/
devTools?: boolean | DevToolsOptions
/**
* The initial state, same as Redux's createStore.
* You may optionally specify it to hydrate the state
* from the server in universal apps, or to restore a previously serialized
* user session. If you use `combineReducers()` to produce the root reducer
* function (either directly or indirectly by passing an object as `reducer`),
* this must be an object with the same shape as the reducer map keys.
*/
/*
Not 100% correct but the best approximation we can get:
- if S is a `CombinedState` applying a second `CombinedState` on it does not change anything.
- if it is not, there could be two cases:
- `ReducersMapObject<S, A>` is being passed in. In this case, we will call `combineReducers` on it and `CombinedState<S>` is correct
- `Reducer<S, A>` is being passed in. In this case, actually `CombinedState<S>` is wrong and `S` would be correct.
As we cannot distinguish between those two cases without adding another generic paramter,
we just make the pragmatic assumption that the latter almost never happens.
*/
preloadedState?: PreloadedState<CombinedState<NoInfer<S>>>
/**
* The store enhancers to apply. See Redux's `createStore()`.
* All enhancers will be included before the DevTools Extension enhancer.
* If you need to customize the order of enhancers, supply a callback
* function that will receive the original array (ie, `[applyMiddleware]`),
* and should return a new array (such as `[applyMiddleware, offline]`).
* If you only need to add middleware, you can use the `middleware` parameter instead.
*/
enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback
}
type Middlewares<S> = ReadonlyArray<Middleware<{}, S>>
/**
* A Redux store returned by `configureStore()`. Supports dispatching
* side-effectful _thunks_ in addition to plain actions.
*
* @public
*/
export interface EnhancedStore<
S = any,
A extends Action = AnyAction,
M extends Middlewares<S> = Middlewares<S>
> extends Store<S, A> {
/**
* The `dispatch` method of your store, enhanced by all its middlewares.
*
* @inheritdoc
*/
dispatch: ExtractDispatchExtensions<M> & Dispatch<A>
}
/**
* A friendly abstraction over the standard Redux `createStore()` function.
*
* @param config The store configuration.
* @returns A configured Redux store.
*
* @public
*/
export function configureStore<
S = any,
A extends Action = AnyAction,
M extends Middlewares<S> = [ThunkMiddlewareFor<S>]
>(options: ConfigureStoreOptions<S, A, M>): EnhancedStore<S, A, M> {
const curriedGetDefaultMiddleware = curryGetDefaultMiddleware<S>()
const {
reducer = undefined,
middleware = curriedGetDefaultMiddleware(),
devTools = true,
preloadedState = undefined,
enhancers = undefined,
} = options || {}
let rootReducer: Reducer<S, A>
if (typeof reducer === 'function') {
rootReducer = reducer
} else if (isPlainObject(reducer)) {
rootReducer = combineReducers(reducer) as unknown as Reducer<S, A>
} else {
throw new Error(
'"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers'
)
}
let finalMiddleware = middleware
if (typeof finalMiddleware === 'function') {
finalMiddleware = finalMiddleware(curriedGetDefaultMiddleware)
if (!IS_PRODUCTION && !Array.isArray(finalMiddleware)) {
throw new Error(
'when using a middleware builder function, an array of middleware must be returned'
)
}
}
if (
!IS_PRODUCTION &&
finalMiddleware.some((item: any) => typeof item !== 'function')
) {
throw new Error(
'each middleware provided to configureStore must be a function'
)
}
const middlewareEnhancer = applyMiddleware(...finalMiddleware)
let finalCompose = compose
if (devTools) {
finalCompose = composeWithDevTools({
// Enable capture of stack traces for dispatched Redux actions
trace: !IS_PRODUCTION,
...(typeof devTools === 'object' && devTools),
})
}
let storeEnhancers: StoreEnhancer[] = [middlewareEnhancer]
if (Array.isArray(enhancers)) {
storeEnhancers = [middlewareEnhancer, ...enhancers]
} else if (typeof enhancers === 'function') {
storeEnhancers = enhancers(storeEnhancers)
}
const composedEnhancer = finalCompose(...storeEnhancers) as any
return createStore(rootReducer, preloadedState, composedEnhancer)
}