我有一个名为createSlice
的redux-toolkit中的getOne
生成的redux-thunk减速器。
getOne
从API提取并调度加载状态(startedLoading
,finishedLoading
,errorLoading
)的操作。
我还要调用另一个在同一个切片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
// { ... }
}
})
}
答案 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