3

I am using nextJS and running into a little problem here and am not sure where I am going wrong. I've built an app where everything works in dev. However, I am trying to build my app but I am receiving the error:

./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

The useRouter is located is on my page called [genre].js and the code is detailed below:

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

The official for Nextjs suggests using the useRouter to access the URL parameters

Why does this error occur? What am I missing? Any workarounds?

Update

I am trying to go with the solution below.

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

However I am still getting the errors below while trying to build my code

**./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**

Would not the switch statement be considered a conditional render? Any workaround for this?

2
  • Please check the second part of my answer, I believe it answers it solves the hooks problem Commented Sep 17, 2022 at 14:27
  • NextJS version ? Commented Nov 5, 2022 at 17:46

2 Answers 2

5

You simply need to rename your component to start with a capital letter, i.e., Genre. The reason for this, is that components must start with uppercase letters, and only components and hooks can make use of hooks (such as useRouter)

export default function Genre()

For the hook problem you can't conditionally call hooks or call them within blocks, they're a lot like import statements, they need to be called at the top of their parents (for the hook, the parent will be the function, i.e., you need to call hooks at the top of the component/function). To still do what you want to do, rather than conditionally calling the hook, you can conditionally pass in parameters using the ternary operator (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator). I recommed reading into hook patterns (https://www.patterns.dev/posts/hooks-pattern/):

  const router = useRouter();
  const { genre } = router.query;
  // instead of calling the hook conditionally, change the parameter you're calling in the hook conditionally, this will fix the error you're getting with regards to conditionally calling hooks

  const mymovies = useFetchTrendingCategory(genre == "Trending" || genre == "Top Rated" ? undefined : genre)

    return (
      <div>
        {/* <Navbar /> */}
        <div>{genre}</div>
        <Moviegenreresults movies={mymovies} />
      </div>
    );
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. That seemed to have solved my problem but it's created another one. I am now getting the error below with the same code snippet as posted above. Any idea why this problem shows up and how to fix it? 21:20 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
Ah, so basically you can't access hooks within blocks (i.e., for loops, if statements, etc.) they need to be called at the top of your function. Please see my updated answer for details about another approach to fixing this problem
reactjs.org/docs/hooks-rules.html#explanation it's a critical issue if you conditionally call a hook. never do it. The explanation is nice and goes into how useState() useX magic works in the first place, it's based on order of execution.
1

I can see 2 problem here.

The first one is the component name (as @Ameer said). Try to rename it to Genre.

Second one is the use of hook in a conditionally render. This is not a good practice.

You can refactor code like this:

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;
  const [movies, setMovies] = useState([]) // is that an array?

  useEffect(() => {
   let fetchedMovies

   switch (genre) {
     case 'Trending':
       fetchedMovies = useFetchTrendingCatagory()
     case 'Top Rated"':
       fetchedMovies = useFetchTopRatedCatagory()
     default:
       fetchedMovies = useFetchMovieGenreResults(genre)
   }

   setMovies(fetchedMovies)
  }, [genre])
    
  return (
    <div>
      {/* <Navbar /> */}
      <div>{genre}</div>
      <Moviegenreresults movies={movies} />
    </div>
  )
}

or like this:

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

1 Comment

Hello @Carmelo I am trying to go with your second solution but I am still having errors when building (although everything works fine when in dev). Would the switch statement still be considered a conditional render which would cause that error to occur? Any suggestions?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.