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

NextJs构建时React Hooks条件调用报错原因及解决方法咨询

Next.js构建时React Hooks规则报错

错误信息

./pages/genre.js/[genre].js
15:14 错误:React Hook "useFetchTrendingCatagory" 被条件调用。React Hooks必须在每次组件渲染时按完全相同的顺序调用。您是否在提前return后意外调用了React Hook?react-hooks/rules-of-hooks
17:14 错误:React Hook "useFetchTopRatedCatagory" 被条件调用。React Hooks必须在每次组件渲染时按完全相同的顺序调用。您是否在提前return后意外调用了React Hook?react-hooks/rules-of-hooks
19:14 错误:React Hook "useFetchMovieGenreResults" 被条件调用。React Hooks必须在每次组件渲染时按完全相同的顺序调用。您是否在提前return后意外调用了React Hook?react-hooks/rules-of-hooks

问题代码

import React from "react";
import { useRouter } from "next/router";
import useFetchMovieGenreResults from "../../hooks/useFetchMovieGenreResults";
import { useState } from "react";
import useFetchTrendingCatagory from "../../hooks/useFetchTrendingCatagory";
import useFetchTopRatedCatagory from "../../hooks/useFetchTopRatedCatagory";

const useMovies = (genre) => {
  switch (genre) {
    case 'Trending':
      return useFetchTrendingCatagory()
    case 'Top Rated"':
      return useFetchTopRatedCatagory()
    default:
      return useFetchMovieGenreResults(genre)
  }
}

export default function Genre () {
  const router = useRouter();
  const { genre } = router.query;
  const mymovies = useMovies(genre)

  return (
    <div>
      {/* <Navbar /> */}
      <div>{genre}</div>
      <Moviegenreresults movies={mymovies} />
    </div>
  )
}

错误原因

违反了React Hooks的核心规则:所有Hooks必须在组件/自定义Hook的顶层按固定顺序调用,不能在条件判断、循环、嵌套函数中调用。

你的useMovies自定义Hook里,根据genre的不同值会调用不同的Hook,这导致组件每次渲染时,调用的Hook数量和顺序可能不一致——比如当genre是Trending时,只会调用useFetchTrendingCatagory,不会调用另外两个Hook;当genre切换为其他值时,调用的Hook又会变化。这种条件调用Hook的行为,会破坏React对Hook状态的追踪逻辑,因此被ESLint的react-hooks/rules-of-hooks规则拦截(开发环境可能没严格校验,但构建阶段会触发错误)。

另外注意代码里的小错误:case 'Top Rated"': 多了一个多余的双引号,需要修正为case 'Top Rated':。

解决办法

方法1:在组件顶层调用所有Hook,再根据genre选择结果

直接在Genre组件的顶层调用所有需要的Hook,确保每次渲染时Hook的调用顺序完全一致,之后再根据genre的值选择对应的数据:

import React from "react";
import { useRouter } from "next/router";
import useFetchMovieGenreResults from "../../hooks/useFetchMovieGenreResults";
import useFetchTrendingCatagory from "../../hooks/useFetchTrendingCatagory";
import useFetchTopRatedCatagory from "../../hooks/useFetchTopRatedCatagory";

export default function Genre () {
  const router = useRouter();
  const { genre } = router.query;
  
  // 顶层调用所有Hook,确保顺序固定
  const trendingMovies = useFetchTrendingCatagory();
  const topRatedMovies = useFetchTopRatedCatagory();
  const genreMovies = useFetchMovieGenreResults(genre);

  // 根据genre选择对应的数据
  const mymovies = genre === 'Trending' 
    ? trendingMovies 
    : genre === 'Top Rated' 
      ? topRatedMovies 
      : genreMovies;

  return (
    <div>
      {/* <Navbar /> */}
      <div>{genre}</div>
      <Moviegenreresults movies={mymovies} />
    </div>
  )
}

方法2:修改自定义Hook,先顶层调用所有Hook再返回结果

如果想保留useMovies这个自定义Hook,可以在其内部先顶层调用所有需要的Hook,再根据genre返回对应结果:

import React from "react";
import { useRouter } from "next/router";
import useFetchMovieGenreResults from "../../hooks/useFetchMovieGenreResults";
import useFetchTrendingCatagory from "../../hooks/useFetchTrendingCatagory";
import useFetchTopRatedCatagory from "../../hooks/useFetchTopRatedCatagory";

const useMovies = (genre) => {
  // 先在顶层调用所有Hook,确保顺序一致
  const trendingMovies = useFetchTrendingCatagory();
  const topRatedMovies = useFetchTopRatedCatagory();
  const genreMovies = useFetchMovieGenreResults(genre);

  switch (genre) {
    case 'Trending':
      return trendingMovies;
    case 'Top Rated': // 修正多余的双引号
      return topRatedMovies;
    default:
      return genreMovies;
  }
}

export default function Genre () {
  const router = useRouter();
  const { genre } = router.query;
  const mymovies = useMovies(genre);

  return (
    <div>
      {/* <Navbar /> */}
      <div>{genre}</div>
      <Moviegenreresults movies={mymovies} />
    </div>
  )
}

方法3:在Hook内部添加条件逻辑,避免无效请求

如果担心调用所有Hook会触发不必要的API请求,可以修改每个Hook,添加一个控制是否执行请求的参数,在不需要请求时跳过数据获取:

比如修改useFetchTrendingCatagory:

// hooks/useFetchTrendingCatagory.js
import { useState, useEffect } from 'react';

export default function useFetchTrendingCatagory(shouldFetch = true) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!shouldFetch) return; // 不满足条件则跳过请求
    const fetchData = async () => {
      setLoading(true);
      try {
        const res = await fetch('你的趋势电影API地址');
        const result = await res.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, [shouldFetch]);

  return { data, loading, error };
}

同理修改useFetchTopRatedCatagory和useFetchMovieGenreResults,然后在useMovies中调用时传入条件参数:

const useMovies = (genre) => {
  const isTrending = genre === 'Trending';
  const isTopRated = genre === 'Top Rated';
  
  // 根据条件控制是否执行请求
  const trendingMovies = useFetchTrendingCatagory(isTrending);
  const topRatedMovies = useFetchTopRatedCatagory(isTopRated);
  const genreMovies = useFetchMovieGenreResults(genre, !isTrending && !isTopRated);

  switch (genre) {
    case 'Trending':
      return trendingMovies;
    case 'Top Rated':
      return topRatedMovies;
    default:
      return genreMovies;
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:25:38