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

使用Redux Toolkit+createAsyncThunk遇类型错误及调度问题求助

React-Redux-Toolkit结合createAsyncThunk开发的TypeScript错误排查与解决

问题代码

categorySlice.js

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import useGetData from "hooks/useGetDate";

const initialState = {
  category: [],
  loading: false,
  error: null,
};

export const getAllCategory = createAsyncThunk(
  "category/getAll",
  async (url, thunkAPI) => {
    const { rejectWithValue } = thunkAPI;
    try {
      const res = await useGetData(url);
      return res.data;
    } catch (err) {
      return rejectWithValue(err.response.data);
    }
  }
);

export const categorySlice = createSlice({
  name: "category",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getAllCategory.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(getAllCategory.fulfilled, (state, action) => {
        state.loading = false;
        state.error = null;
        state.category = action.payload; // Assuming action.payload is an array of categories
      })
      .addCase(getAllCategory.rejected, (state, action) => {
        state.loading = false;
        state.error = action?.error?.message;
      });
  },
});

export default categorySlice.reducer;

AllCategoryPage.js

import { useEffect } from "react";
import CategoryContainer from "../../Components/Category/CategoryContainer";
import Pagination from "../../Components/Uitily/Pagination";
import { useSelector, useDispatch } from "react-redux";
import { getAllCategory } from "redux/categorySlice";

const AllCategoryPage = () => {
  const dispatch = useDispatch();
  const data = useSelector((state) => state.categoryData.category);
  const isLoading = useSelector((state) => state.categoryData.loading);
  const error = useSelector((state) => state.categoryData.error);

  useEffect(() => {
    dispatch(getAllCategory("/api/v1/categories"));
  }, [dispatch]);

  return (
    <div style={{ minHeight: "670px" }}>
      <CategoryContainer />
      <Pagination />
    </div>
  );
};

export default AllCategoryPage;

错误信息

错误1(Code: 2554)

Expected 0 arguments, but got 1.

错误2(Code: 2345)

Argument of type 'AsyncThunkAction<any, void, AsyncThunkConfig>' is not assignable to parameter of type 'AnyAction'.

错误3(Code: 2339)

Property 'categoryData' does not exist on type 'unknown'.

问题原因解析

  1. 错误1(2554):

    • 在createAsyncThunk的回调函数中调用了React自定义Hook useGetData,违反了React Hook的调用规则(Hook只能在组件或其他自定义Hook的顶层调用)。
    • 非法调用导致TypeScript无法正确推断getAllCategory的参数类型,误以为它不需要接收参数,传参时触发类型错误。
  2. 错误2(2345):

    • 未给Redux配置正确的TypeScript类型,useDispatch默认返回Dispatch<AnyAction>类型,无法识别createAsyncThunk生成的AsyncThunkAction,导致类型不匹配。
  3. 错误3(2339):

    • 组件中访问state.categoryData,但Redux store中并未注册名为categoryData的reducer,slice名称为category,若注册时用category作为键名,应访问state.category。
    • 未定义RootState类型,useSelector无法推断state结构,默认视为unknown类型,因此报错属性不存在。

可行解决方案

1. 修复Hook非法调用问题

将useGetData替换为普通API请求函数(不能是Hook),若useGetData封装了axios/fetch逻辑,抽成普通函数:

修改categorySlice.js:

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
// 导入普通API函数而非Hook
import { getData } from "services/api";

const initialState = {
  category: [],
  loading: false,
  error: null,
};

export const getAllCategory = createAsyncThunk(
  "category/getAll",
  async (url, thunkAPI) => {
    const { rejectWithValue } = thunkAPI;
    try {
      const res = await getData(url); // 调用普通函数
      return res.data;
    } catch (err) {
      return rejectWithValue(err.response?.data || "请求失败");
    }
  }
);

// 其余slice代码保持不变

2. 配置Redux TypeScript类型

在Redux store配置文件(如store.ts)中定义RootState和AppDispatch类型,导出自定义的useDispatch和useSelector:

store.ts:

import { configureStore } from "@reduxjs/toolkit";
import categoryReducer from "./categorySlice";

export const store = configureStore({
  reducer: {
    category: categoryReducer, // 键名与slice对应,组件中需访问state.category
    // 其他reducer...
  },
});

// 定义全局状态类型
export type RootState = ReturnType<typeof store.getState>;
// 定义支持异步action的dispatch类型
export type AppDispatch = typeof store.dispatch;

// 导出自定义hooks,简化类型使用
import { useDispatch, useSelector } from "react-redux";
import type { TypedUseSelectorHook } from "react-redux";

export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

3. 修正组件中的state访问路径及dispatch调用

修改AllCategoryPage.js,使用自定义hooks并修正state访问路径:

import { useEffect } from "react";
import CategoryContainer from "../../Components/Category/CategoryContainer";
import Pagination from "../../Components/Uitily/Pagination";
// 导入自定义hooks而非redux原生hooks
import { useAppDispatch, useAppSelector } from "redux/store";
import { getAllCategory } from "redux/categorySlice";

const AllCategoryPage = () => {
  const dispatch = useAppDispatch();
  // 访问state.category,与store中reducer键名一致
  const data = useAppSelector((state) => state.category.category);
  const isLoading = useAppSelector((state) => state.category.loading);
  const error = useAppSelector((state) => state.category.error);

  useEffect(() => {
    dispatch(getAllCategory("/api/v1/categories"));
  }, [dispatch]);

  return (
    <div style={{ minHeight: "670px" }}>
      <CategoryContainer />
      <Pagination />
    </div>
  );
};

export default AllCategoryPage;

额外注意事项

  • 若项目启用TypeScript检查,建议将组件改为.tsx后缀,slice/store改为.ts后缀,获得更完整的类型支持。
  • 可在createAsyncThunk中显式指定泛型参数,明确返回值和参数类型,例如:
    export const getAllCategory = createAsyncThunk<Category[], string>(
      "category/getAll",
      async (url, thunkAPI) => {
        // ...
      }
    );
    
    其中Category为自定义的分类数据类型。

内容的提问来源于stack exchange,提问作者eslam wael

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 09:04:53