React-Redux问题:dispatch无法将API数据存入state
问题描述
刚接触Redux,正在项目中练习使用。API调用已成功获取数据并保存到data(第27行已打印),但第28行打印的Redux state中的AnimeList.data却是空数组,请问这是什么原因?
AnimeList.js
import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { useGetTrendingAnimeListQuery } from '../../services/animeApi'; import { addData } from './AnimeListSlice'; import AnimeCard from '../AnimeCard/AnimeCard'; import AnimeSelector from '../AnimeSelector/AnimeSelector'; import { Spin } from 'antd'; import './AnimeList.css'; const AnimeList = () => { const AnimeList = useSelector((state) => state.AnimeList.data); const dispatch = useDispatch(); const { data, isFetching } = useGetTrendingAnimeListQuery(); useEffect(() => { if (!isFetching) { dispatch(addData(data)); } }, []); console.log(data); console.log(AnimeList); const loadingCards = [1, 2, 3, 4, 5]; return ( <div className='container'> {!isFetching ? <div className="wrapper"> <AnimeSelector /> <div className='anime-list-wrapper'> {AnimeList.map((anime) => ( <AnimeCard anime={anime} key={anime.id} /> ))} </div> </div> : <div className='wrapper'> <div className='anime-list-wrapper'> {loadingCards.map(() => ( <div className='loading-card'> <Spin /> </div> ))} </div> </div> } </div> ) } export default AnimeList;
AnimeListSlice.js
import { createSlice } from "@reduxjs/toolkit"; const initialState = { data: [] }; export const AnimeListSlice = createSlice({ name: 'AnimeList', initialState, reducers: { addData: (state, action) => { state.data = action.payload; }, } }); export const { addData } = AnimeListSlice.actions; export default AnimeListSlice.reducer;
animeApi.js
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; const animeApiHeaders = { "Accept": "application/vnd.api+json", "Content-Type": "application/vnd.api+json", }; const baseUrl = 'https://kitsu.io/api/edge'; const createRequest = (url) => ({ url, headers: animeApiHeaders }); export const animeApi = createApi({ reducerPath: 'animeApi', baseQuery: fetchBaseQuery({ baseUrl }), endpoints: (builder) => ({ getTrendingAnimeList: builder.query({ query: () => createRequest('/trending/anime?limit=20'), }), getAllAnimeList: builder.query({ query: () => createRequest('/anime?limit=20'), }), }) }); export const { useGetTrendingAnimeListQuery, useGetAllAnimeList } = animeApi;
问题原因与修复方案
核心原因
你的useEffect依赖项是空数组,导致它只会在组件首次挂载时执行一次。此时API请求还未完成:isFetching仍为true,data还是初始的undefined,所以不会触发dispatch(addData(data)),Redux state保持初始的空数组状态。
后续API请求完成后,data和isFetching发生变化,但useEffect因为没有监听这两个依赖项,不会重新执行,所以数据永远不会被存入Redux。
另外,Kitsu API的响应结构是外层包含data字段的对象(比如{ data: [...], meta: ... }),你直接把整个响应对象传给addData的话,最终state.AnimeList.data会是这个对象,而非实际的动漫数组,后续渲染时AnimeList.map会报错。
修复步骤
- 修正useEffect依赖项:将
data和isFetching加入依赖数组,确保状态变化时重新执行dispatch:
useEffect(() => { // 同时判断data是否存在,避免初始时传入undefined if (!isFetching && data) { dispatch(addData(data.data)); // 取响应中的实际数据数组 } }, [data, isFetching, dispatch]); // 添加依赖
正确提取API数据:在dispatch时传入
data.data,拿到实际的动漫列表数组。可选优化:其实RTK Query本身已经把数据缓存到Redux中,你可以直接通过
useGetTrendingAnimeListQuery的返回值获取数据,不需要额外手动存入自己的slice,这样能减少冗余代码。比如直接用data?.data来渲染:
// 去掉useSelector和useEffect相关代码 return ( <div className='container'> {!isFetching ? <div className="wrapper"> <AnimeSelector /> <div className='anime-list-wrapper'> {data?.data.map((anime) => ( <AnimeCard anime={anime} key={anime.id} /> ))} </div> </div> : // 加载状态... } </div> )
内容的提问来源于stack exchange,提问作者notoriousRorious
相关产品推荐
相关产品推荐

