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

React电影应用跳转详情页时渲染失败并报错的问题

React电影详情页加载报错解决方案

错误原因

  • 初始渲染时,movieDetailData是空对象,直接访问description.length、genres[0].name等属性会因值为undefined抛出TypeError。
  • movieTrailerData初始为空数组,访问movieTrailerData[0].title同样会触发报错。
  • 热重载时组件重新渲染,此时数据已加载完成,所以注释再取消注释能正常显示;但刷新页面后数据重新请求,初始状态回到空值,问题复现。

解决方案

方案一:父组件添加加载状态控制

在请求数据完成前,显示加载指示器,避免渲染未准备好的数据:

import React, {useState, useEffect} from "react";
import {Box, CircularProgress} from "@mui/material";
import {useParams} from "react-router-dom";
import MovieTrailers from "../components/MovieTrailers";
import Details from "../components/Details";
import {fetchData, options} from "../utils/fetchData";

function MovieDetails() {
    
    const [movieDetailData, setMovieDetailData] = useState({});
    const [movieTrailerData, setMovieTrailerData] = useState([]);
    const [isLoading, setIsLoading] = useState(true);
    const {id} = useParams();
   
    useEffect(()=>{
        const fetchMovieData = async ()=> {
            setIsLoading(true);
            try {
                const detailData = await fetchData(`https://movies-app1.p.rapidapi.com/api/movie/${id}`, options);
                setMovieDetailData(detailData.result);

                const trailerData = await fetchData(`https://movies-app1.p.rapidapi.com/api/trailers/${id}`, options);
                setMovieTrailerData(trailerData.result);
            } catch (err) {
                console.error("请求数据失败:", err);
            } finally {
                setIsLoading(false);
            }
        }
        fetchMovieData();
    },[id]);
    
    if(isLoading) {
        return <Box display="flex" justifyContent="center" alignItems="center" minHeight="80vh">
            <CircularProgress />
        </Box>;
    }
    
    return(
    <Box>
        <Details movieDetailData={movieDetailData} />
        <MovieTrailers movieTrailerData={movieTrailerData} />
    </Box>)
}

export default MovieDetails;

方案二:子组件添加空值安全访问

使用可选链操作符(?.)和空值合并运算符(??),处理未定义的属性和空数组情况:

修改Details组件

import React from "react";
import {Box, Stack, Typography, Chip} from "@mui/material";

function Details(props) {
    const{image, titleOriginal, countries, genres, rating, release, description} = props.movieDetailData;

    return <Stack direction="row" width="100%" sx={{backgroundColor:"#fff"}}>
        {image && <img className="movie-poster" src={image} alt="movie-poster"/>}
        <Stack p="50px" sx={{width:"60%"}}>
        <Typography variant="h2" fontWeight="700">
           {titleOriginal ?? "暂无标题"}
        </Typography>
        <Box pt="30px" sx={{display:"flex", gap:"30px"}}>
        <Chip label={<Typography variant="h6" fontWeight="700">{`rating : ${rating ?? "暂无评分"}`}</Typography>} />
        <Chip label={<Typography variant="h6" fontWeight="700">{`release: ${release ?? "暂无上映信息"}`}</Typography>} />
        </Box>
        <Typography variant="h6" pt="30px">
        {description?.length>600 ? description.substring(0, 601) + "..." : description ?? "暂无简介"}
        </Typography>
        <Box pt="30px" sx={{display:"flex", gap:"30px"}}>
        <Chip label={<Typography variant="h6" fontWeight="700">{`genre : ${genres?.[0]?.name ?? "暂无类型"}`}</Typography>} />
        <Chip label={<Typography variant="h6" fontWeight="700">{`origin : ${countries?.[0]?.name ?? "暂无产地"}`}</Typography>} />
        </Box>
    </Stack>
</Stack>
}

export default Details;

修改MovieTrailers组件

import React from "react";
import {Box, Stack, Typography} from "@mui/material";
import TrailerCard from "./TrailerCard";

function MovieTrailers({movieTrailerData}) {
    return <Box p="30px">
        <Typography variant="h4" pt="40px" pb="50px" fontWeight="700" m="auto">
            {`Watch ${movieTrailerData?.[0]?.title ?? "相关预告片"}`}
        </Typography>
        <Stack direction="row" justifyContent="space-between" flexWrap="wrap" gap="20px">
        {(movieTrailerData?.slice(0, 3) || []).map((trailer)=>(
            <TrailerCard key={trailer.url} trailer={trailer}/>
        ))}
        </Stack>
    </Box>
}

export default MovieTrailers;

修改TrailerCard组件

import React from "react";
import {Box, Stack, Typography, Chip} from "@mui/material";

function TrailerCard({trailer}) {
    const{thumbnail, ago, author, views, title, url, description} = trailer;

    return <Box className="trailer-card" p="10px" backgroundColor="#fff" width="25%">
        <Stack>
        <a
        className="movie-trailer-link"
        href={url}
        target="_blank"
        rel="noreferrer">
            {thumbnail && <img className="movie-trailer-thumbnail" src={thumbnail} alt="movie-trailer"/>}
        <Typography pt="10px" variant="body1" fontWeight="700" >
            {title ?? "暂无标题"}
        </Typography>
        <Stack paddingBlock="10px" direction="row" justifyContent="space-between" gap="10px" flexWrap="wrap">
        <Chip label={`views: ${views ?? "0"}`} />
        <Chip label={`ago: ${ago ?? "未知"}`}/>
        </Stack>
        <Typography variant="subtitle2">{`YT: ${author?.name ?? "未知作者"}`}</Typography>
        <Typography variant="body2" pb="20px">
        {description ?? "暂无描述"}
        </Typography>
        </a>
        </Stack>
    </Box>
}

export default TrailerCard;

关键优化点

  • 加载状态:通过isLoading控制渲染逻辑,避免在数据未就绪时渲染子组件。
  • 可选链?.:安全访问嵌套属性,防止因属性不存在抛出错误。
  • 空值合并??:为未定义的属性提供默认值,提升用户体验。
  • 唯一key:在列表循环中添加唯一标识,符合React渲染规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 19:39:49