Issue
I'm returning to Redux + Redux Toolkit to manage some React app state.
I'm trying to get my head around generic reducers, specifically for slices of state that share the same reducer logic and state structure. Namely an "uploaded files" slice, representing files that have been uploaded to a server/local store. Having read this section of the docs about re-using reducer logic, I came up with this higher-order reducer:
import { createSlice, PayloadAction, SliceCaseReducers, ValidateSliceCaseReducers } from '@reduxjs/toolkit'
interface UploadFilesState {
files: {
type: 'font'|'image'
fileName: string
filePath: string
}[]
maxFiles: number
}
export const createUploadFilesSlice = <
Reducers extends SliceCaseReducers<UploadFilesState>
>({
name = '',
initialState,
reducers,
}: {
name: string
initialState: UploadFilesState
reducers?: ValidateSliceCaseReducers<UploadFilesState, Reducers>
}) => {
return createSlice({
name,
initialState,
reducers: {
fileUploaded(state: UploadFilesState, action: PayloadAction<UploadFilesState['files'][0]>) {
if (state.files.length < state.maxFiles) {
state.files.push(action.payload)
}
},
...reducers,
},
})
}
But I'm stumped as to how to use it with configureStore()
and the state structure I would like. For example, my global state should look something like this:
// Written as a Typescript interface to visualise the shape of the store
{
appName: string
defaults: {
background: {
colour: string
images?: /* Using my generic uploadFileSlice here */
}
fonts: /* Using my generic uploadFileSlice here */
}
}
How would I utilise my genric slices with Redux Toolkit's configureStore()
method? For example, something like this:
export default configureStore({
reducer: {
appName: /* Some string reducer */
defaults: {
background: {
colour: /* some colour reducer */
images: createUploadFilesSlice({ name: 'defaultBackgroundImages', initialState: { files: [], maxFiles: 1}})
}
fonts: createUploadFilesSlice({ name: 'defaultFonts', initialState: { files: [], maxFiles: 2}})
}
},
})
The above throws. Is it that I can't be allowed to nest reducers in object literals in this way? Should I be instead creating very flat structures like:
{
appName: string
defaultBackgroundColour: string
defaultBackgroundImages: /* Using my generic uploadFileSlice here */
defaultFonts: /* Using my generic uploadFileSlice here */
}
It's been a while since I've used Redux so I'm unfamiliar with what the limitations are.
Solution
The primary issue here is that the Redux core combineReducers
method is not recursive - it accepts an object with a single level of sliceName: someSliceReducer
pairs. When the reducer
field in configureStore
is given an object, it really just passes that on to combineReducers
so you don't have to call that yourself.
If you want to have some additional levels of nesting that way, you'd need to call combineReducers
yourself a couple of times, like this:
const backgroundReducer = combineReducers({
color: colorReducer,
images: createSomeReusableReducer("images")
})
const defaultsReducer = combineReducers({
background: backgroundReducer,
fonts: createSomeReusableReducer("fonts")
})
const store = configureStore({
reducer: {
appName: appNameReducer,
defaults: defaultsReducer
}
})
Answered By - markerikson
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.