异步调用中Redux Store应何时更新?拖拽用createAsyncThunk更新状态延迟如何解决?
问题根源
你当前的实现是等待后端接口请求成功后才更新本地Redux状态,状态更新完全依赖网络请求速度,只要有网络波动或者接口响应慢,就会出现拖拽完成后界面很久才变化的卡顿问题。
解决方案:使用乐观更新
核心逻辑是:拖拽结束后先立即更新本地状态让用户看到效果,再异步发送请求同步到后端,若请求失败再回滚本地状态即可,完全消除等待延迟。
步骤1:修改Slice代码,新增同步操作
const todoBlockSlice = createSlice({ name: "todoBlockSlice", initialState: { sections: [] as TodoSectionType[], // 存储更新前的状态快照,用于请求失败后回滚 lastSectionsSnapshot: [] as TodoSectionType[] }, reducers: { // 拖拽后立即更新本地状态的同步action updateDragStateOptimistic: (state, action) => { // 先保存当前状态快照 state.lastSectionsSnapshot = [...state.sections]; const { src, dest } = action.payload; // 将原来写在fulfilled中的更新逻辑挪到这里 state.sections = state.sections.map(sec => { if (sec.sectionType === src.sectionType) return { ...src }; if (dest && sec.sectionType === dest.sectionType) return { ...dest }; return sec; }) }, // 请求失败时回滚状态的同步action rollbackDragState: (state) => { state.sections = state.lastSectionsSnapshot; } }, extraReducers: (builder) => { // 请求失败时自动回滚状态,可自行添加toast等失败提示 builder.addCase(updateDragNDrop.rejected, (state) => { todoBlockSlice.caseReducers.rollbackDragState(state); }) } }) export const { updateDragStateOptimistic, rollbackDragState } = todoBlockSlice.actions;
步骤2:修改组件的拖拽结束逻辑
// Component code const onDragEnd = () => { // 原有排序逻辑,得到newOrderObjArray // 第一步:立即调用同步action更新状态,用户无感知延迟 dispatch(updateDragStateOptimistic(newOrderObjArray)); // 第二步:异步发送请求同步后端,不需要等待返回 dispatch(updateDragNDrop(newOrderObjArray)); }
步骤3:优化异步Thunk的错误处理
//Async Thunk Code export const updateDragNDrop = createAsyncThunk( "todoBlockSlice/updateStateAfterDragNDrop", async (sections: any, { rejectWithValue }) => { try { const sectionsArr = [sections.src, sections.dest] await axios.post("http://localhost:8080/updateDnd", sectionsArr); return sections; } catch (err) { return rejectWithValue(err); } } );
注意事项
- 若存在用户短时间内多次拖拽的场景,可以给
createAsyncThunk添加AbortController逻辑,取消上一次未完成的请求,避免旧请求失败触发错误回滚。 - 请求失败时建议添加 toast 等用户提示,避免用户误以为操作成功但后端未保存数据。
内容的提问来源于stack exchange,提问作者Shail
相关产品推荐
相关产品推荐

