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

TypeScript结合Redux使用Dispatch API遇阻,请求技术协助

问题修正方案

核心问题分析

你的代码主要有几个关键错误,导致dispatch无法正常工作:

  1. React Hooks不能在异步Thunk中使用:useAppDispatch是React Hook,只能在函数组件或自定义Hook内调用,不能直接放在createAsyncThunk的异步逻辑里。
  2. API响应数据结构处理错误:Themealdb的API返回的是包含meals字段的对象,不是直接的字符串数组,你定义的类型和数据提取逻辑都错了。
  3. 异步逻辑的实现方式不符合Redux Toolkit规范:createAsyncThunk的正确用法是返回数据,然后通过slice的extraReducers处理异步状态,而非在Thunk内部手动dispatch。
  4. 类型定义不匹配:MealState中的meal字段应该是餐品对象数组,不是字符串数组。

修正后的完整代码

1. Redux Slice文件(meal.ts)

import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import axios from 'axios';

// 定义餐品数据的接口,根据API返回结构补充字段
export interface Meal {
  idMeal: string;
  strMeal: string;
  strMealThumb: string;
  // 其他需要的字段可自行添加
}

export interface MealState {
  meal: Meal[];
  input: string;
  favorites: string[];
  categories: string[];
  loading: boolean; // 新增:异步加载状态
  error: string | null; // 新增:错误状态
}

const initialState: MealState = {
  meal: [],
  input: '',
  favorites: [],
  categories: [],
  loading: false,
  error: null,
};

// 正确定义异步Thunk:仅返回数据,由extraReducers处理结果
export const getMeal = createAsyncThunk<Meal[], string>(
  'meal/getMeal',
  async (search: string) => {
    const response = await axios.get(`https://www.themealdb.com/api/json/v1/1/search.php?s=${search}`);
    // 处理API返回null的情况(无匹配结果时)
    return response.data.meals || [];
  }
);

const mealSlice = createSlice({
  name: 'meal',
  initialState,
  reducers: {
    addInput: (state, action: PayloadAction<string>) => {
      state.input = action.payload;
    },
  },
  // 处理异步Thunk的三种状态:pending/fulfilled/rejected
  extraReducers: (builder) => {
    builder
      .addCase(getMeal.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(getMeal.fulfilled, (state, action: PayloadAction<Meal[]>) => {
        state.loading = false;
        state.meal = action.payload;
      })
      .addCase(getMeal.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message || '获取餐品数据失败';
      });
  },
});

export default mealSlice.reducer;
export const { addInput } = mealSlice.actions;

2. Header组件文件

import { useEffect, useState } from 'react';
import { useAppDispatch, useAppSelector } from '../redux/hooks';
import { getMeal, addInput } from '../redux/meal';
import { RootState } from '../redux/store';
import { Box, TextField } from '@mui/material';

const Header = () => {
  const dispatch = useAppDispatch();
  const input = useAppSelector((state: RootState) => state.meal.input);
  const [inputText, setInputText] = useState(input);

  const inputHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    setInputText(e.target.value.toLowerCase());
  };

  useEffect(() => {
    // 添加防抖,避免输入时频繁触发API请求
    const timer = setTimeout(() => {
      dispatch(getMeal(inputText));
    }, 300);

    dispatch(addInput(inputText));

    return () => clearTimeout(timer);
  }, [dispatch, inputText]);

  return (
    <Box sx={{ m: 3 }}>
      <TextField
        onChange={inputHandler}
        placeholder="Search"
        sx={{ mb: 3 }}
        value={inputText} // 设为受控组件,保证输入框值与state同步
      />
    </Box>
  );
};

export default Header;

关键修正说明

  • 移除了Thunk内部的useAppDispatch调用,改用Redux Toolkit的extraReducers自动处理异步结果的dispatch。
  • 修正了API数据的类型定义和提取逻辑,正确处理无匹配结果时的null值。
  • 添加了loading和error状态,便于处理异步加载过程中的UI反馈。
  • 给输入框添加防抖逻辑,减少不必要的API请求。
  • 修正了输入框的事件类型,符合TypeScript规范,同时将输入框设为受控组件。

内容的提问来源于stack exchange,提问作者Yohav Rn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 14:35:26