redux-toolkit使用另一个thunk reducer在同一片中使用一个actionCreater

时间:2019-12-22 21:23:15

标签: redux redux-thunk

我有一个名为createSlice的redux-toolkit中的getOne生成的redux-thunk减速器。

getOne从API提取并调度加载状态(startedLoadingfinishedLoadingerrorLoading)的操作。

我还要调用另一个在同一个切片insert中创建的actionCreater,并生成结果数据。或直接通过getOne减速器更新状态。

import { createSlice } from "@reduxjs/toolkit"
import { startedLoading, finishedLoading, errorLoading } from '../slices/loadingSlice'
const apiEndpoint = "/api/v1"
const fetchOptions = { headers: { "Content-Type": "application/json" } }

const createModelSlice = function (modelName) {
  return createSlice({
    name: modelName,
    initialState: {byId: {}},
    reducers: {
      getOne: (state, action) => async (dispatch) => {
        const { id, options } = action.payload
        const url = `${apiEndpoint}/${modelName}/${id}`
        const storeKey = `${modelName}_${id}`
        dispatch(startedLoading({ key: storeKey }))
        let response

        try {
          response = await fetch(url, fetchOptions)
        } catch (error) {
          dispatch(errorLoading({ key: storeKey }))
          throw new Error(error)
        }

        const data = await response.json()
        if (!response.ok) {
          dispatch(errorLoading({ key: storeKey }))
          throw new Error(`${response.status} loading ${url}`, { response, data, modelName, id, options })
        } else {

          // How would I update the store here?

          // 1. reference the insert actionCreater somehow.
          dispatch(insert({id: id, data: data))

          // 2. construct the action manually
          dispatch({action: `${modelName}/insert`, payload: {id: id, data: data))

          // 3. Mutate the state here and rely immer. (I'm not sure exactly how that works)
          state[modelName].byId[id] = data

          dispatch(finishedLoading({ key: storeKey }))
        }
      },
      insert: (state, action) => {
        const { id, data } = action.payload
        return {
          ...state,
          byId: {
            ...state.byId,
            [id ?? data.id]: data
          }
        }
      },
      // More reduceres
      // { ... }
    }
  })
}

1 个答案:

答案 0 :(得分:0)

我错过了文档中关于切片无法使用thunk的部分。无论哪种方式,它都不会起作用,因为重击动作不会映射到化简器,而是使用其他多个化简器/动作。

创建切片后,我在切片操作中添加了thunk操作。这样我可以参考其他动作


import { createSlice } from "@reduxjs/toolkit"
const slice = createSlice({
  name: name,
  initialState: { byId: {} },
  reducers: { /* */ }
}
slice.actions.myThunkAction = payload => async (dispatch, state) => {
  // ...
  slice.actions.nonThunkAction({ id: id, data: data})
  slice.actions.anotherNonThunkAction({ index payload.index, data: data.map( /* */ )})
}
import { createSlice } from "@reduxjs/toolkit"
import { startedLoading, finishedLoading, errorLoading } from '../slices/loadingSlice'
import encodeURLParams from '../tools/encodeURLParams'

const apiEndpoint = "/api/v1"
const fetchOptions = { headers: { "Content-Type": "application/json" } }

const createModelSlice = function (modelName) {
  const slice = createSlice({
    name: modelName,
    initialState: { byId: {} },
    reducers: {
      insert: (state, action) => {
        const { id, data } = action.payload
        return {
          ...state,
          byId: {
            ...state.byId,
            [id ?? data.id]: data
          }
        }
      },
      bulkInsert: (state, action) => {
        return {
          ...state,
          byId: {
            ...state.byId,
            ...action.payload.reduce((accumulator, item) => ({
              ...accumulator, [item.id]: item
            }), { [action.payload[0].id]: action.payload[0] })
          }
        }
      },
      createIndex: (state, action) => {
        const { name, ids } = action.payload
        return { ...state, [name]: ids }
      },
    }
  })
  slice.actions.loadMany = payload => async (dispatch, state) => {
    const { params, index } = payload
    const storeKey = `${modelName}_${index}`
    const url = encodeURLParams(`${apiEndpoint}/${modelName}`, params)
    dispatch(startedLoading({ key: storeKey }))
    let response

    try {
      response = await fetch(url, fetchOptions)
    } catch (error) {
      dispatch(errorLoading({ key: storeKey }))
      throw new Error(error)
    }

    const data = await response.json()
    if (!response.ok) {
      dispatch(errorLoading({ key: storeKey }))
      throw new Error(`${response.status} loading ${url}`, { response, data, modelName, params, options })
    } else {
      dispatch(slice.actions.bulkInsert(data))
      dispatch(slice.actions.createIndex({
        ids: data.map(model => model.id),
        name: index
      }))
      dispatch(finishedLoading({ key: storeKey }))
    }
  }
  slice.actions.loadOne = payload => async (dispatch, state) => {
    const { id, options } = payload
    const url = `${apiEndpoint}/${modelName}/${id}`
    const storeKey = `${modelName}_${id}`
    dispatch(startedLoading({ key: storeKey }))
    let response

    try {
      response = await fetch(url, fetchOptions)
    } catch (error) {
      dispatch(errorLoading({ key: storeKey }))
      throw new Error(error)
    }

    const data = await response.json()
    if (!response.ok) {
      dispatch(errorLoading({ key: storeKey }))
      throw new Error(`${response.status} loading ${url}`, { response, data, modelName, id, options })
    } else {
      dispatch(slice.actions.insert({ id, data }))

      dispatch(finishedLoading({ key: storeKey }))
    }
  }
  return slice
}

export default createModelSlice