I'm trying to make typings work for configuring a Redux store with a preloaded state.
The Redux Toolkit TypeScript quick start guide has this example:
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({
reducer: {
one: oneSlice.reducer,
two: twoSlice.reducer
}
})
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
Unfortunately with a preloaded state, it looks more like this:
export function initStore(preloadedState) {
const store = configureStore({
reducer: {
one: oneSlice.reducer,
two: twoSlice.reducer
},
preloadedState,
})
return store
}
From where do I now get the RootState
type and the AppDispatch
type?
You can infer the type of your state based on the argument types of your reducers. We will want to separate the reducer
value into a separate const
in order to use typeof
on just the reducer.
const reducer = {
one: oneSlice.reducer,
two: twoSlice.reducer
};
You are using an object of slice reducers rather than a function created combineReducers
. Redux toolkit exports a utility type that we can use to infer the state from reducer map object notation.
import { StateFromReducersMapObject } from "@reduxjs/toolkit";
export type RootState = StateFromReducersMapObject<typeof reducer>
We could have just as well gotten the type for the Store
by looking at the ReturnType
of initStore
and then gotten the RootState
by looking at the ReturnType
from the store's getState
method. That would be the most similar to the example. This same approach also allows us to get the type for AppDispatch
. Note that we use bracket notation instead of dot notation because our Store
is a type
, not an object
.
type Store = ReturnType<typeof initStore>
type RootState = ReturnType<Store['getState']>
type AppDispatch = Store['dispatch']
The advantage of separating the reducer
outside of initStore
is that we can now use the types from the reducer to declare the appropriate type for the preloadedState
argument, which was not typed before.
import { configureStore, Slice, StateFromReducersMapObject, PreloadedState } from "@reduxjs/toolkit";
const reducer = {
one: oneSlice.reducer,
two: twoSlice.reducer
};
export type RootState = StateFromReducersMapObject<typeof reducer>
export function initStore(preloadedState?: PreloadedState<RootState>) {
return configureStore({
reducer,
preloadedState,
});
}
type Store = ReturnType<typeof initStore>
export type AppDispatch = Store['dispatch']
Typescript Playground Link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With