React项目Invalid Hook Call错误排查求助
问题分析与修复方案
核心错误点
1. 无效钩子调用(Invalid Hook Call)
- Movies组件中错误地直接调用
<Navigate>组件,而非使用useNavigate钩子实现编程式导航。 - 导航逻辑完全错误:点击单部电影的"SEE MORE"时,不应传递整个movies数组作为路由参数,而是传递当前电影的
imdbID。
2. "map is undefined"错误
- Moviecard组件中,通过
i参数请求OMDB单部电影详情时,接口返回的是单个对象而非数组,你错误地赋值了data.Search(该字段仅在s搜索接口返回数组时存在),导致desc变为undefined,调用map时抛出错误。 - Movies组件中,OMDB返回的电影唯一标识是
imdbID而非id,使用movie.id作为key会导致警告,且可能引发渲染问题。
修复后的Movies组件代码
import axios from "axios"; import React, { useEffect, useState } from "react"; import Nav from "../ui/Nav"; import { useNavigate, useParams } from "react-router-dom"; const API_URL = "http://www.omdbapi.com/?apikey=cca6a59"; function Movies() { const [movies, setMovies] = useState([]); const { id: title } = useParams(); const [loading, setLoading] = useState(true); const navigate = useNavigate(); // 使用useNavigate钩子实现跳转 // 点击SEE MORE时跳转到对应电影详情页 const handleSeeMore = (imdbID) => { navigate(`/moviecard/${imdbID}`); }; async function fetchMovies(searchTitle) { try { const { data } = await axios.get(`${API_URL}&s=${searchTitle}`); // 处理接口无结果的情况,避免setMovies为undefined setMovies(data.Search || []); setLoading(false); } catch (err) { console.error("获取电影列表失败:", err); setMovies([]); setLoading(false); } } useEffect(() => { if (title) { fetchMovies(title); } }, [title]); return ( <> <Nav /> <div className="movie__row"> <div className="movie__wrapper"> {loading ? new Array(8).fill(0).map((_, index) => ( <div className="movie" key={index}> <div className="movie__img--skeleton"> <div className="movie__title--skeleton"> <div className="movie__year--skeleton"></div> </div> </div> </div> )) : movies.map((movie) => ( <div className="movie" key={movie.imdbID}> {/* 使用正确的imdbID作为key */} <div className="movie__img"> <img src={movie.Poster} alt={movie.Title} /> <div className="movie__content"> <h1>{movie.Title}</h1> <h1>{movie.Year}</h1> {/* 传递当前电影的imdbID */} <p onClick={() => handleSeeMore(movie.imdbID)}>SEE MORE</p> </div> </div> </div> ))} </div> </div> </> ); } export default Movies;
修复后的Moviecard组件代码
import { useParams } from "react-router-dom"; import Nav from "../ui/Nav"; import { useEffect, useState } from "react"; import axios from "axios"; const API_URL = "http://www.omdbapi.com/?apikey=cca6a59"; function Moviecard() { const { id: imdbID } = useParams(); const [movie, setMovie] = useState(null); // 单部电影是对象,初始值设为null const [loading, setLoading] = useState(true); async function fetchDesc() { try { const { data } = await axios.get(`${API_URL}&i=${imdbID}`); // 直接赋值整个返回对象,而非data.Search setMovie(data); setLoading(false); } catch (err) { console.error("获取电影详情失败:", err); setMovie(null); setLoading(false); } } useEffect(() => { if (imdbID) { fetchDesc(); } }, [imdbID]); // 添加imdbID作为依赖,参数变化时重新请求 return ( <> <Nav /> {loading ? ( // 展示合理的骨架屏,避免使用未定义的movie属性 <div className="movie__img--wrapper"> <div className="skeleton-title"></div> <div className="skeleton-img"></div> </div> ) : movie ? ( // 直接渲染单个电影对象,无需map <div className="movie__info--wrapper" key={movie.imdbID}> <h1>{movie.Title}</h1> <img src={movie.Poster} alt={movie.Title} /> <h3> <span className="red">Released: </span>{movie.Released} </h3> <h3> <span className="red">Actors: </span>{movie.Actors} </h3> <h3> <span className="red">Genre: </span>{movie.Genre} </h3> <h3> <span className="red">Director: </span>{movie.Director} </h3> <h3> <span className="red">Writer: </span>{movie.Writer} </h3> <h3> <span className="red">Language: </span>{movie.Language} </h3> <h3> <span className="red">Plot: </span>{movie.Plot} </h3> </div> ) : ( <p>电影详情未找到</p> )} </> ); } export default Moviecard;
额外优化点
- 添加了错误捕获逻辑,避免接口请求失败导致页面崩溃。
- 处理了接口无结果的边界情况,防止
movies或movie为undefined。 - 修正了路由参数的解构写法,提高代码可读性。
- 为图片添加了正确的
alt属性,符合无障碍标准。
内容的提问来源于stack exchange,提问作者Cam Watkins
相关产品推荐
相关产品推荐

