網站搜尋

reactjs - 將 Redux Toolkit 通用高階 reducer 組合成具有意義的狀態層級,並使用 configureStore


我正在回到 Redux + Redux Toolkit 來管理一些 React 應用程式的狀態。

我正在嘗試理解通用 reducers,特別是針對共享相同 reducer 邏輯和狀態結構的 state 切片。也就是所謂的「已上傳文件」切片,表示已經上傳到伺服器/本地存儲的文件。在閱讀了文檔中關於重用 reducer 邏輯的這一部分後,我設計出了這個高階 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,
    },
  })
}

但我對如何將它與 configureStore() 以及我想要的狀態結構一起使用感到困惑。例如,我的全局狀態應該看起來像這樣:

// 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 */
  }
}

我如何使用 Redux Toolkit 的 configureStore() 方法來利用我的通用 slice?例如,像這樣的方式:

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}})
    }
  },
})

上述拋出。是不是我不可以這樣在物件字面量中巢狀使用 reducers?我是否應該改為創建像這樣非常平坦的結構:

{
  appName: string
  defaultBackgroundColour: string
  defaultBackgroundImages: /* Using my generic uploadFileSlice here */
  defaultFonts: /* Using my generic uploadFileSlice here */
}

自從我使用 Redux 已經有一段時間了,所以我不熟悉它的限制是什麼。

答案#1

這裡的主要問題是 Redux 核心的 combineReducers 方法不是遞歸的——它接受一個只包含單層 sliceName: someSliceReducer 配對的物件。當在 configureStore 中的 reducer 欄位給定一個物件時,它實際上只是將這個物件傳給 combineReducers,所以你不需要自己去呼叫它。

如果你想要以這種方式有一些額外的巢狀層級,你需要自己調用 combineReducers 幾次,就像這樣:

const backgroundReducer = combineReducers({
  color: colorReducer,
  images: createSomeReusableReducer("images")
})

const defaultsReducer = combineReducers({
  background: backgroundReducer,
  fonts: createSomeReusableReducer("fonts")
})

const store = configureStore({
  reducer: {
    appName: appNameReducer,
    defaults: defaultsReducer
  }
})