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

React Redux中GameSpot API请求已完成但组件数据为undefined

问题:Redux DevTools显示数据已获取,但React组件中数据为undefined

基于Vite搭建的React Redux应用,使用GameSpot API,Redux DevTools显示三个查询(games、reviews、releases)状态均为fulfilled且能看到数据,但组件中渲染时数据是undefined。目前在App.jsx的useEffect中合并三个端点的字段:

  • games:获取genres、release date
  • reviews:获取scores
  • releases:获取platforms、publishers、developers

相关代码

App.jsx

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import { useDispatch } from 'react-redux'
import { useEffect } from 'react'
import './App.css'
import { setGames } from './features/Games/gamesSlice'
import { useGetGamesQuery, useGetReviewsQuery, useGetReleasesQuery } from './api/gamespotApiSlice'
import { Header } from './features/Header/Header'
import { GamesList } from './features/Games/GamesList/GamesList'
import { GameDetails } from './features/Games/GameDetails/GameDetails'

function App () {
  const dispatch = useDispatch();
  const { data: gamesData } = useGetGamesQuery();
  const { data: reviewsData } = useGetReviewsQuery();
  const { data: releasesData } = useGetReleasesQuery();
  useEffect(() => {
    if (gamesData && reviewsData && releasesData) {
      const mergedData = gamesData.results.map(game => {
        const review = reviewsData.results.find(review => review.game.id === game.id);
        const release = releasesData.results.find(release => release.game_api_url.includes(game.name));
        const publisher = release && release.publishers.length > 0 ? release.publishers[0] : "Unknown";
        const developer = release && release.developers.length > 0 ? release.developers[0] : "Unknown";
        return {
            ...game,
            id: game.id,
            name: game.name,
            cover: game.image,
            platform: release ? release.platform : null,
            genres: game.genres || [],
            release_year: new Date(release ? release.release_date : null).getFullYear(),
            score: review ? review.score : null,
            publisher,
            developer
        };
      });
      const sortedGames = [...mergedData].sort((a, b) => b.score - a.score);
      dispatch(setGames(sortedGames));
    }
  }, [dispatch, gamesData, reviewsData, releasesData]);
  return (
    <Router>
      <Header />
      <main>
        <section className='game-view'>
          <Routes>
            <Route path='/games/:id' element={<GameDetails />} />
            <Route path='/' element={<GamesList />} />
          </Routes>
        </section>
      </main>
    </Router>
  );
}

export default App;

gamespotApiSlice.js

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

const API_KEY = '?api_key='+ import.meta.VITE_GAMESPOT_API_KEY;
const jsonFormat = '&format=json';

export const gamespotApi = createApi({
    reducerPath: 'gamespotApi',
    baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
    endpoints: (builder) => ({
        getGames: builder.query({
            query: () => `games/${API_KEY}${jsonFormat}`,
        }),
        getReviews: builder.query({
            query: () => `reviews/${API_KEY}${jsonFormat}&sort=score:desc`,
        }),
        getReleases: builder.query({
            query: () => `releases/${API_KEY}${jsonFormat}`,
        })
    })
})

export const {
    useGetGamesQuery,
    useGetReviewsQuery,
    useGetReleasesQuery
} = gamespotApi;

export default gamespotApi.reducer;

排障与解决方案

1. 校验RTK Query返回的数据结构

GameSpot API的响应通常会嵌套在response字段下,而非直接返回results。先在useEffect中打印完整数据结构确认:

console.log('完整games数据:', gamesData);
console.log('完整reviews数据:', reviewsData);
console.log('完整releases数据:', releasesData);

如果实际数据路径是data.response.results,那之前的gamesData.results会直接返回undefined,导致合并逻辑失效。

2. 确认组件的Redux数据读取路径

检查GamesList/GameDetails组件是否正确通过useSelector读取store中的数据:

// 示例:GamesList组件的正确读取方式
import { useSelector } from 'react-redux';

const GamesList = () => {
  // 确认state路径与gamesSlice中的定义一致
  const games = useSelector(state => state.games.games);
  
  // 数据未加载完成时显示加载状态
  if (!games) return <div>加载中...</div>;
  
  return (
    // 游戏列表渲染逻辑
  );
};

如果store路径错误(比如写成state.games.data而非实际的state.games.games),组件会拿到undefined。

3. 修复跨端点数据匹配逻辑

(1)替换名称匹配为ID匹配

release.game_api_url.includes(game.name)极易因为名称大小写、副标题、空格差异匹配失败,改用游戏ID匹配更可靠:

// 从game_api_url中提取游戏ID(假设URL格式为"/api/games/12345")
const release = releasesData.results.find(release => {
  const gameId = release.game_api_url.split('/').pop();
  return parseInt(gameId) === game.id;
});

(2)修复日期处理异常

当release.release_date为null或无效格式时,new Date(null).getFullYear()会返回1970,需添加判断:

release_year: release && release.release_date 
  ? new Date(release.release_date).getFullYear() 
  : "未知",

4. 用RTK Query自定义查询简化合并流程

可以跳过useEffect手动合并,直接在API slice中创建合并数据的自定义查询:

// 在gamespotApiSlice.js中添加
getMergedGames: builder.query({
  async queryFn(_, _queryApi, _extraOptions, baseQuery) {
    // 并行请求三个接口
    const [gamesRes, reviewsRes, releasesRes] = await Promise.all([
      baseQuery(`games/${API_KEY}${jsonFormat}`),
      baseQuery(`reviews/${API_KEY}${jsonFormat}&sort=score:desc`),
      baseQuery(`releases/${API_KEY}${jsonFormat}`)
    ]);

    // 处理请求错误
    if (gamesRes.error || reviewsRes.error || releasesRes.error) {
      return { error: '数据获取失败' };
    }

    // 提取有效数据(注意路径要和实际响应匹配)
    const games = gamesRes.data.response.results;
    const reviews = reviewsRes.data.response.results;
    const releases = releasesRes.data.response.results;

    // 合并数据
    const mergedData = games.map(game => {
      const review = reviews.find(r => r.game.id === game.id);
      const release = releases.find(r => {
        const gameId = r.game_api_url.split('/').pop();
        return parseInt(gameId) === game.id;
      });

      return {
        ...game,
        cover: game.image,
        platform: release?.platform || null,
        genres: game.genres || [],
        release_year: release?.release_date ? new Date(release.release_date).getFullYear() : "未知",
        score: review?.score || null,
        publisher: release?.publishers?.[0] || "未知",
        developer: release?.developers?.[0] || "未知"
      };
    });

    // 按评分排序
    const sortedGames = mergedData.sort((a, b) => (b.score || 0) - (a.score || 0));
    return { data: sortedGames };
  }
})

然后在App.jsx中直接使用该查询:

const { data: mergedGames } = useGetMergedGamesQuery();

useEffect(() => {
  if (mergedGames) {
    dispatch(setGames(mergedGames));
  }
}, [dispatch, mergedGames]);

5. 添加全局加载状态

在App.jsx中添加加载状态判断,避免数据未准备好时渲染undefined:

const { data: gamesData, isLoading: gamesLoading } = useGetGamesQuery();
const { data: reviewsData, isLoading: reviewsLoading } = useGetReviewsQuery();
const { data: releasesData, isLoading: releasesLoading } = useGetReleasesQuery();

const isLoading = gamesLoading || reviewsLoading || releasesLoading;

// 加载中时显示占位
if (isLoading) return <div>加载游戏数据中...</div>;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 17:32:04