React+Redux Toolkit调用TMDB API详情页数据返回Undefined求助
React电影应用详情页API数据返回Undefined问题修复
问题现状
使用React.js开发电影Web应用,通过TMDB API获取列表数据正常,但点击电影卡片跳转至详情页后,调用API返回Undefined,控制台显示数据为Undefined,采用Redux Toolkit进行状态管理。
核心问题原因
- TMDB接口返回结构处理错误:TMDB的
/movie/{id}单电影详情接口返回的是单个电影对象,而非列表接口那样包含results数组的结构,但异步action中错误地返回了response.data.results,导致获取不到有效数据。 - 组件依赖项缺失:Detail组件的
useEffect未将路由参数id加入依赖数组,路由参数变化时无法重新触发数据请求。 - 异步操作日志时机错误:在
useEffect中直接打印entity,此时异步请求尚未完成,拿到的是初始状态的空对象。
修复步骤
1. 修正异步Action的返回值
修改action.js中的fetchMovie函数,直接返回接口返回的单个电影对象:
import { createAsyncThunk } from "@reduxjs/toolkit"; import { movieList } from "../../service/api/api"; export const fetchMovieAll = createAsyncThunk('movie/fetchMovieAll', async() => { try { const api = `api_key=${import.meta.env.VITE_TMDB_KEY}` const response = await movieList.get(`discover/movie?${api}`) return response.data.results } catch (error) { return error } }) export const fetchMovie = createAsyncThunk('movie/fetchMovie', async(id) => { try { const response = await movieList.get(`/movie/${id}?api_key=${import.meta.env.VITE_TMDB_KEY}`) // 单电影接口返回单个对象,无results字段 return response.data } catch (error) { return error } })
2. 完善Detail组件的依赖与日志逻辑
修改Detail.jsx,补充依赖数组并调整日志时机:
import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { useParams } from 'react-router-dom' import { fetchMovie } from '../../manage/action' function Detail() { const {id} = useParams() const dispatch = useDispatch() const {entity, loading} = useSelector((state) => state.movieDb) const fetchDataMovie = (movieId) => { dispatch(fetchMovie(movieId)) } // 路由id变化时重新请求数据 useEffect(() => { fetchDataMovie(id) }, [id, dispatch]) // 监听entity变化后再打印 useEffect(() => { console.log(entity) }, [entity]) return ( <> {loading ? <div>加载中...</div> : ( <div> <h1>{entity.title || '电影详情'}</h1> {/* 可添加更多详情字段展示,例如海报、简介等 */} </div> )} </> ) } export default Detail
3. 修正Slice中的错误状态逻辑
修改slice.js中请求失败时的loading状态(原代码错误设置为true):
import {createSlice} from '@reduxjs/toolkit' import * as movieAction from '../action' const movieDb = createSlice({ name: 'movieDb', initialState: { loading: false, data: [], entity: { id: null, title: '', release_date: '', popularity: '', poster_path: '', overview: '', vote_average: '', backdrop_path: '' }, error: null, }, extraReducers: (builder) => builder.addCase(movieAction.fetchMovieAll.pending, (state) => { state.loading = true }) .addCase(movieAction.fetchMovieAll.fulfilled, (state, action) => { state.loading = false state.data = action.payload }) .addCase(movieAction.fetchMovieAll.rejected, (state, action) => { state.loading = false state.error = action.payload }) .addCase(movieAction.fetchMovie.pending, (state) => { state.loading = true }) .addCase(movieAction.fetchMovie.fulfilled, (state, action) => { state.loading = false state.entity = action.payload }) .addCase(movieAction.fetchMovie.rejected, (state, action) => { state.loading = false state.error = action.payload }) }) export default movieDb.reducer;
额外优化建议
- 在CardMovie组件的
useEffect中添加dispatch作为依赖,避免潜在的陈旧闭包问题:
useEffect(() => { fetchDataMovie() }, [dispatch])
- 可以根据
loading状态添加加载动画,提升用户体验。
内容的提问来源于stack exchange,提问作者Hovi Sohibul Wafa
相关产品推荐
相关产品推荐

