You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的报错。

附带问题

  1. Reducer中存在非法副作用:在updateSelectedOrNewMovementData.fulfilled的reducer里使用setTimeout修改state、调用window.history.back(),违反了Redux reducer必须是纯函数的规则,会导致状态更新不可预测。
  2. 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/.rejected action类型。
  • Redux reducer必须是纯函数,不能包含定时器、DOM操作、API调用、dispatch等副作用。
  • 多个addCase可以链式调用同一个builder,不需要拆分多个builder实例。

内容的提问来源于stack exchange,提问作者Arman Ebrahimi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:07:49