UNPKG

5.39 kBPlain TextView Raw
1import {
2 createStore,
3 compose,
4 applyMiddleware,
5 combineReducers,
6 Reducer,
7 ReducersMapObject,
8 Middleware,
9 Action,
10 AnyAction,
11 StoreEnhancer,
12 Store,
13 DeepPartial,
14 Dispatch
15} from 'redux'
16import {
17 composeWithDevTools,
18 EnhancerOptions as DevToolsOptions
19} from './devtoolsExtension'
20
21import isPlainObject from './isPlainObject'
22import {
23 ThunkMiddlewareFor,
24 curryGetDefaultMiddleware,
25 CurriedGetDefaultMiddleware
26} from './getDefaultMiddleware'
27import { DispatchForMiddlewares } from './tsHelpers'
28
29const IS_PRODUCTION = process.env.NODE_ENV === 'production'
30
31/**
32 * Callback function type, to be used in `ConfigureStoreOptions.enhancers`
33 *
34 * @public
35 */
36export type ConfigureEnhancersCallback = (
37 defaultEnhancers: StoreEnhancer[]
38) => StoreEnhancer[]
39
40/**
41 * Options for `configureStore()`.
42 *
43 * @public
44 */
45export interface ConfigureStoreOptions<
46 S = any,
47 A extends Action = AnyAction,
48 M extends Middlewares<S> = Middlewares<S>
49> {
50 /**
51 * A single reducer function that will be used as the root reducer, or an
52 * object of slice reducers that will be passed to `combineReducers()`.
53 */
54 reducer: Reducer<S, A> | ReducersMapObject<S, A>
55
56 /**
57 * An array of Redux middleware to install. If not supplied, defaults to
58 * the set of middleware returned by `getDefaultMiddleware()`.
59 */
60 middleware?: ((getDefaultMiddleware: CurriedGetDefaultMiddleware<S>) => M) | M
61
62 /**
63 * Whether to enable Redux DevTools integration. Defaults to `true`.
64 *
65 * Additional configuration can be done by passing Redux DevTools options
66 */
67 devTools?: boolean | DevToolsOptions
68
69 /**
70 * The initial state, same as Redux's createStore.
71 * You may optionally specify it to hydrate the state
72 * from the server in universal apps, or to restore a previously serialized
73 * user session. If you use `combineReducers()` to produce the root reducer
74 * function (either directly or indirectly by passing an object as `reducer`),
75 * this must be an object with the same shape as the reducer map keys.
76 */
77 // NOTE: The needlessly complicated `S extends any ? S : S` instead of just
78 // `S` ensures that the TypeScript compiler doesn't attempt to infer `S`
79 // based on the value passed as `preloadedState`, which might be a partial
80 // state rather than the full thing.
81 preloadedState?: DeepPartial<S extends any ? S : S>
82
83 /**
84 * The store enhancers to apply. See Redux's `createStore()`.
85 * All enhancers will be included before the DevTools Extension enhancer.
86 * If you need to customize the order of enhancers, supply a callback
87 * function that will receive the original array (ie, `[applyMiddleware]`),
88 * and should return a new array (such as `[applyMiddleware, offline]`).
89 * If you only need to add middleware, you can use the `middleware` parameter instead.
90 */
91 enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback
92}
93
94type Middlewares<S> = ReadonlyArray<Middleware<{}, S>>
95
96/**
97 * A Redux store returned by `configureStore()`. Supports dispatching
98 * side-effectful _thunks_ in addition to plain actions.
99 *
100 * @public
101 */
102export interface EnhancedStore<
103 S = any,
104 A extends Action = AnyAction,
105 M extends Middlewares<S> = Middlewares<S>
106> extends Store<S, A> {
107 /**
108 * The `dispatch` method of your store, enhanced by all its middlewares.
109 *
110 * @inheritdoc
111 */
112 dispatch: DispatchForMiddlewares<M> & Dispatch<A>
113}
114
115/**
116 * A friendly abstraction over the standard Redux `createStore()` function.
117 *
118 * @param config The store configuration.
119 * @returns A configured Redux store.
120 *
121 * @public
122 */
123export function configureStore<
124 S = any,
125 A extends Action = AnyAction,
126 M extends Middlewares<S> = [ThunkMiddlewareFor<S>]
127>(options: ConfigureStoreOptions<S, A, M>): EnhancedStore<S, A, M> {
128 const curriedGetDefaultMiddleware = curryGetDefaultMiddleware<S>()
129
130 const {
131 reducer = undefined,
132 middleware = curriedGetDefaultMiddleware(),
133 devTools = true,
134 preloadedState = undefined,
135 enhancers = undefined
136 } = options || {}
137
138 let rootReducer: Reducer<S, A>
139
140 if (typeof reducer === 'function') {
141 rootReducer = reducer
142 } else if (isPlainObject(reducer)) {
143 rootReducer = combineReducers(reducer)
144 } else {
145 throw new Error(
146 '"reducer" is a required argument, and must be a function or an object of functions that can be passed to combineReducers'
147 )
148 }
149
150 const middlewareEnhancer = applyMiddleware(
151 ...(typeof middleware === 'function'
152 ? middleware(curriedGetDefaultMiddleware)
153 : middleware)
154 )
155
156 let finalCompose = compose
157
158 if (devTools) {
159 finalCompose = composeWithDevTools({
160 // Enable capture of stack traces for dispatched Redux actions
161 trace: !IS_PRODUCTION,
162 ...(typeof devTools === 'object' && devTools)
163 })
164 }
165
166 let storeEnhancers: StoreEnhancer[] = [middlewareEnhancer]
167
168 if (Array.isArray(enhancers)) {
169 storeEnhancers = [middlewareEnhancer, ...enhancers]
170 } else if (typeof enhancers === 'function') {
171 storeEnhancers = enhancers(storeEnhancers)
172 }
173
174 const composedEnhancer = finalCompose(...storeEnhancers) as any
175
176 return createStore(rootReducer, preloadedState as any, composedEnhancer)
177}
178
\No newline at end of file