Redux Toolkit报错Uncaught TypeError: typeOrActionCreator未定义求助
Redux Toolkit extraReducers 报错:TypeError: typeOrActionCreator is undefined 解决方法
核心错误原因
你的updateSelectedOrNewMovementData没有通过Redux Toolkit的createAsyncThunk创建异步thunk,而是直接定义了一个普通异步函数,导致它不存在.fulfilled/.pending/.rejected这些属性。当builder.addCase尝试读取这些属性时,拿到的是undefined,就触发了typeOrActionCreator is undefined的报错。
附带问题
- Reducer中存在非法副作用:在
updateSelectedOrNewMovementData.fulfilled的reducer里使用setTimeout修改state、调用window.history.back(),违反了Redux reducer必须是纯函数的规则,会导致状态更新不可预测。 - Reducer中直接dispatch:在
createNewMovement.fulfilled的reducer里调用dispatch,同样不符合reducer的纯函数要求,异步逻辑的后续dispatch应该放在thunk内部处理。
修复后的代码示例
1. 正确创建异步thunk
import { createAsyncThunk } from '@reduxjs/toolkit'; // 用createAsyncThunk创建标准异步thunk export const updateSelectedOrNewMovementData = createAsyncThunk( "myMovements/updateSelectedOrNewMovementData", async(allData) => { const {data, type, motion_id} = allData try{ const {receivedData} = await patchRequest(`/coach/motion/${motion_id}`, data) return type } catch (err) { console.error("err", err.response.data.message); // 抛出错误,让rejected分支捕获 throw err.response.data.message; } }); // createNewMovement同样需要用createAsyncThunk创建,并在内部处理后续dispatch export const createNewMovement = createAsyncThunk( "myMovements/createNewMovement", async(formData, { dispatch }) => { try { const response = await postRequest('/coach/motion', formData); if (response.data.is_successful) { const motionId = response.data.motion_id; // 在thunk内部触发后续请求 await dispatch(updateSelectedOrNewMovementData({ data: formData.myForm, type: "formData", motion_id: motionId })); await dispatch(updateSelectedOrNewMovementData({ data: formData.textFormBody, type: "rawData", motion_id: motionId })); } return response.data; } catch (err) { console.error("err", err.response.data.message); throw err.response.data.message; } } );
2. 修正extraReducers逻辑
extraReducers(builder) { // 单个builder链式调用所有case即可,无需拆分多个builder实例 builder .addCase(handleGetMyMovements.fulfilled, (state, action) => { const data = action.payload; if (data?.length > 0) { state.myMovements = [...state.myMovements, ...data]; state.myMovementsTemp = [...state.myMovements, ...data]; } state.status.isNotMoreData = data?.length >= 20; state.status.myMovementsLoading = false; }) .addCase(handleGetMyMovements.pending, (state) => { state.status.myMovementsLoading = true; }) .addCase(updateSelectedOrNewMovementData.fulfilled, (state, action) => { if (action?.payload === "formData") { state.status.selectedOrNewMovementLoading = "sentData"; } }) .addCase(updateSelectedOrNewMovementData.pending, (state) => { // pending状态的action.payload不存在,需通过thunk的meta传递标识或在组件处理 state.status.selectedOrNewMovementLoading = true; }) .addCase(updateSelectedOrNewMovementData.rejected, (state) => { state.status.selectedOrNewMovementLoading = "error"; }) .addCase(createNewMovement.fulfilled, (state, action) => { state.status.selectedOrNewMovementLoading = false; if (action.payload?.is_successful) { state.motionId = action.payload.motion_id; } }) .addCase(createNewMovement.pending, (state) => { state.status.selectedOrNewMovementLoading = true; }) .addCase(createNewMovement.rejected, (state) => { state.status.selectedOrNewMovementLoading = "error"; }); }
3. 组件中处理副作用逻辑
将定时器、页面跳转等副作用移到组件中,通过监听状态变化触发:
import { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { resetLoadingStatus } from './yourSlice'; // 自定义重置状态的action function YourComponent() { const loadingStatus = useSelector(state => state.yourSlice.status.selectedOrNewMovementLoading); const dispatch = useDispatch(); useEffect(() => { if (loadingStatus === "sentData") { const timer = setTimeout(() => { window.history.back(); dispatch(resetLoadingStatus()); // 跳转后重置状态 }, 3000); return () => clearTimeout(timer); } }, [loadingStatus, dispatch]); // ...组件其他逻辑 }
关键注意事项
- 所有异步操作必须通过
createAsyncThunk创建,才能生成对应的.fulfilled/.pending/.rejectedaction类型。 - Redux reducer必须是纯函数,不能包含定时器、DOM操作、API调用、dispatch等副作用。
- 多个
addCase可以链式调用同一个builder,不需要拆分多个builder实例。
内容的提问来源于stack exchange,提问作者Arman Ebrahimi
相关产品推荐
相关产品推荐

