问题描述
我有一个 redux 切片实体,我用它来存储数组的状态。数组本身包含对象数组的嵌套属性。现在我需要在 redux saga 中更新该状态,并且我试图创建另一个生成器函数,但无法弄清楚如何更新状态。我只需要更新状态,稍后将进行 API 调用。如何更新状态?
这里是task和deliveryParcels的接口
export interface IRiderTask {
_id?: any;
riderId: string;
totalAmount: number;
riderName: string;
cityId: string;
cityName: string;
status: string;
adminName: string;
adminId: string;
createdAt: Date;
deliveryParcels: IDeliveryParcels[];
}
export interface IDeliveryParcels {
parcelId: string;
processingStatus?: string;
amount: number;
orderType: 'COD' | 'NONCOD';
postedStatus?: {
status: string;
statusKey: string;
signature?: string;
checkBoxData?: any[];
reason: string;
adminId?: string;
adminName?: string;
};
}
我每次都会用不同的值更新postedStatus对象,所以需要在saga的生成器函数中处理它。
这是我的传奇
import {
createAsyncThunk,createEntityAdapter,createSelector,createSlice,EntityState,PayloadAction,} from '@reduxjs/toolkit';
import { IRiderTask } from '@swyft/interfaces';
import { put,takeEvery } from 'redux-saga/effects';
import { select } from 'typed-redux-saga/dist';
import { DeliveryManagementState } from '../state';
export const finalStatus_FEATURE_KEY = 'finalStatus';
type Processing = 'initial' | 'processing' | 'processed' | 'processing error';
/*
* Update these interfaces according to your requirements.
*/
export interface finalStatusEntity extends IRiderTask {
_id?: any;
}
export interface finalStatusstate extends EntityState<finalStatusEntity> {
loadingStatus: Processing;
updatingRequest: Processing;
error: string | null;
}
export const finalStatusAdapter = createEntityAdapter<finalStatusEntity>({
selectId: (e) => e._id,});
export const initialfinalStatusstate: finalStatusstate = finalStatusAdapter.getinitialState(
{
loadingStatus: 'initial',updatingRequest: 'initial',error: null,}
);
export const finalStatusSlice = createSlice({
name: finalStatus_FEATURE_KEY,initialState: initialfinalStatusstate,reducers: {
add: finalStatusAdapter.addOne,remove: finalStatusAdapter.removeOne,setLoading: (state,action: PayloadAction<Processing>) => {
state.loadingStatus = action.payload;
},setTasksstate: (state,action: PayloadAction<{ data: any }>) => {},setUpdatedState: (state,action: PayloadAction<Processing>) => {
state.updatingRequest = action.payload;
},},});
/*
* Export reducer for store configuration.
*/
export const finalStatusReducer = finalStatusSlice.reducer;
export const finalStatusActions = finalStatusSlice.actions;
const { selectAll,selectEntities } = finalStatusAdapter.getSelectors();
export const getfinalStatusstate = (
rootState: DeliveryManagementState
): finalStatusstate => rootState.deliveryPilet.finalStatus;
export const selectAllfinalStatus = createSelector(
getfinalStatusstate,selectAll
);
export const selectfinalStatusEntities = createSelector(
getfinalStatusstate,selectEntities
);
export function* finalStatusRootSaga() {
yield takeEvery('finalStatus/setTasksstate',setTasksstate);
yield takeEvery('finalStatus/updateTaskState',updateTaskState);
}
function* setTasksstate({ payload: { data } }: { payload: { data: any } }) {
console.log('In slice',data);
yield put(finalStatusActions.add(data));
console.log(data);
}
function* updateTaskState(action: PayloadAction<{ id: string; data: any }>) {
yield put(finalStatusActions.setUpdatedState('processing'));
yield put(
finalStatusActions.setUpdatedState({
id: action.payload.id,...action.payload.data,})
);
yield put(finalStatusActions.setUpdatedState('processed'));
}
解决方法
这里有两个不同的问题。一种是创建操作和减速器案例以正确更新数据。另一个是通过 saga 调度其中的一些动作。坦率地说,我真的不明白你的传奇应该做什么,我不确定你是否也这样做。但我绝对可以做出一些改进。
“处理”状态适用于整个切片,还是适用于每个任务?
与其通过更新数组的元素来更新parcel,我认为最好将它们视为一个单独的实体。
传奇
import { EntityId,PayloadAction,Update } from "@reduxjs/toolkit";
import { finalStatusActions,FinalStatusEntity } from "./slice";
import { put,all,takeEvery,call } from "redux-saga/effects";
// dummy for API call
const postUpdates = async (
id: EntityId,changes: Partial<FinalStatusEntity>
): Promise<Partial<FinalStatusEntity>> => {
return changes;
};
function* updateTaskState(action: PayloadAction<Update<FinalStatusEntity>>) {
const { id,changes } = action.payload;
try {
const result = yield call(postUpdates,id,changes);
yield put(
finalStatusActions.updateTaskStateSuccess({
id,changes: { ...result,status: "processed" }
})
);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
yield put(
finalStatusActions.updateTaskStateFailure({ id,error: message })
);
}
}
export function* finalStatusRootSaga() {
yield all([
takeEvery(finalStatusActions.updateTaskState.type,updateTaskState)
]);
}
export default finalStatusRootSaga;
商店
import {
AnyAction,combineReducers,configureStore,getDefaultMiddleware
} from "@reduxjs/toolkit";
import {
createSelectorHook,useDispatch as useBasicDispatch
} from "react-redux";
import finalStatus,{ parcelSlice } from "./slice";
import createSagaMiddleware from "redux-saga";
import mySaga from "./saga";
const sagaMiddleware = createSagaMiddleware();
const deliveryPilet = combineReducers({
finalStatus,parcels: parcelSlice.reducer
});
const store = configureStore({
reducer: {
deliveryPilet
},middleware: [...getDefaultMiddleware(),sagaMiddleware]
});
sagaMiddleware.run(mySaga);
export type RootState = ReturnType<typeof store.getState>;
export type DeliveryManagementState = RootState;
export type AppDispatch = typeof store.dispatch;
export type Action = AnyAction;
export const useDispatch = (): AppDispatch => useBasicDispatch();
export const useSelector = createSelectorHook<RootState>();
export default store;
任务
// helper for adding a task
const taskToEntity = (task: IRiderTask): FinalStatusEntity => ({
...task,parcelIds: task.deliveryParcels.map((o) => o.parcelId)
});
export const finalStatusAdapter = createEntityAdapter<FinalStatusEntity>({
selectId: (entity) => entity._id
});
export const initialfinalStatusState = finalStatusAdapter.getInitialState();
export const finalStatusSlice = createSlice({
name: finalStatus_FEATURE_KEY,initialState: initialfinalStatusState,reducers: {
// accept in the form of an IRiderTask instead of FinalStatusEntity
addTask: (state,action: PayloadAction<IRiderTask>) => {
finalStatusAdapter.addOne(state,taskToEntity(action.payload));
},removeTask: finalStatusAdapter.removeOne,updateTaskState: (
state,action: PayloadAction<Update<FinalStatusEntity>>
) => {
// here we just start the update,the saga will dispatch the rest
const { id } = action.payload;
finalStatusAdapter.updateOne(state,{
id,changes: { status: "processing" }
});
},updateTaskStateSuccess: finalStatusAdapter.updateOne,updateTaskStateFailure: (
state,action: PayloadAction<{ id: EntityId; error: string }>
) => {
const { id,error } = action.payload;
finalStatusAdapter.updateOne(state,changes: { status: "processing error",error }
});
}
}
});
export const finalStatusActions = finalStatusSlice.actions;
export default finalStatusReducer;
包裹
export const parcelAdapter = createEntityAdapter<IDeliveryParcels>({
selectId: (parcel) => parcel.parcelId
});
export const parcelSlice = createSlice({
name: "parcels",initialState: parcelAdapter.getInitialState(),reducers: {
updateParcel: parcelAdapter.updateOne,// handle nested update
updatePostedStatus: (
state,action: PayloadAction<Update<IPostedStatus>>
) => {
const { id,changes } = action.payload;
const current = state.entities[id];
if (current) {
current.postedStatus = {
...current.postedStatus,...changes
} as IPostedStatus; // I don't know why this is necessary,but I'm getting TS errors
}
}
},extraReducers: {
// add the parcels when adding a task
[finalStatusActions.addTask.type]: (
state,action: PayloadAction<IRiderTask>
) => {
parcelAdapter.upsertMany(state,action.payload.deliveryParcels);
}
}
});
类型
import { EntityId } from "@reduxjs/toolkit";
export interface IRiderTask {
_id: EntityId;
riderId: string;
totalAmount: number;
riderName: string;
cityId: string;
cityName: string;
status: string;
adminName: string;
adminId: string;
createdAt: Date;
deliveryParcels: IDeliveryParcels[];
}
export interface IPostedStatus {
status: string;
statusKey: string;
signature?: string;
checkboxData?: any[];
reason: string;
adminId?: string;
adminName?: string;
}
export interface IDeliveryParcels {
parcelId: string;
processingStatus?: string;
amount: number;
orderType: "COD" | "NONCOD";
postedStatus?: IPostedStatus;
}
export type Processing =
| "initial"
| "processing"
| "processed"
| "processing error";
// replace parcel objects with ids,include error
export type FinalStatusEntity = Omit<IRiderTask,"deliveryParcels"> & {
parcelIds: string[];
error?: string;
};