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

Next.js构建报错:useRouter及自定义Hook调用违反规则

Next.js构建时React Hooks报错的原因与解决办法

初始问题描述

开发Next.js应用时,开发环境正常,但执行构建出现报错:

./pages/genre.js/[genre].js
13:18 Error: React Hook "useRouter" is called in function "genre" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use". 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";

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

  
  if (genre == "Trending") {
    let mymovies = useFetchTrendingCatagory();

    return (
      <div>
        {/* <Navbar /> */}
        <div>{genre}</div>
        <Moviegenreresults movies={mymovies} />
      </div>
    );
  } else if (genre == "Top Rated") {
    let mymovies = useFetchTopRatedCatagory();

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

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

初始报错原因

  1. 组件命名不符合规范:React要求函数组件名称必须以大写字母开头,你的组件命名为genre(小写开头),ESLint规则判定它不是合法的React组件,因此在其中调用useRouter Hook违反了Hooks使用规则。
  2. Hooks被条件调用:在if/else分支中分别调用不同的自定义Hook,违反了React Hooks核心规则——Hooks必须在组件顶层调用,不能在条件语句、循环、嵌套函数中调用,确保每次渲染时Hooks的调用顺序完全一致。

初始问题解决方向

  • 修正组件名称为大写开头的Genre;
  • 将所有Hook调用移到组件顶层,避免条件性调用。

更新后的问题描述

尝试修改后代码如下:

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>
  )
}

但构建仍报错:

./pages/genre.js/[genre].js
15:14 Error: React Hook "useFetchTrendingCatagory" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? react-hooks/rules-of-hooks
17:14 Error: React Hook "useFetchTopRatedCatagory" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? react-hooks/rules-of-hooks
19:14 Error: React Hook "useFetchMovieGenreResults" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? react-hooks/rules-of-hooks

更新后报错原因

是的,switch语句属于条件判断逻辑,在其中调用Hook依然违反了Hooks调用规则——不管是if/else还是switch,只要是条件分支里的Hook调用,都会导致不同渲染周期中Hook的调用顺序不一致,React无法正确追踪Hook状态。

最终解决办法

必须确保所有自定义Hook都在顶层作用域调用,然后根据genre的值选择使用对应的结果,而不是条件性调用Hook。具体实现如下:

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选择对应的电影数据
  let mymovies;
  if (genre === "Trending") {
    mymovies = trendingMovies;
  } else if (genre === "Top Rated") {
    mymovies = topRatedMovies;
  } else {
    mymovies = genreMovies;
  }

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

补充说明

  • 这种方式虽然会同时调用三个自定义Hook,但如果你的Hook内部做了缓存(比如用useSWR、react-query或自定义缓存逻辑),不会重复发起无效请求;
  • 若担心不必要的请求,可以在自定义Hook内部根据传入参数判断是否执行请求逻辑,比如useFetchMovieGenreResults可在genre为空时不发起请求。

内容的提问来源于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 10:30:43