Time2learnagain
Time2learnagain

Reputation: 31

Error: React Hook "useRouter" - nextJs/javascript app not building

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?

Upvotes: 3

Views: 3758

Answers (2)

Ameer
Ameer

Reputation: 2000

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

Upvotes: 5

Carmelo Catalfamo
Carmelo Catalfamo

Reputation: 93

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

Upvotes: 1

Related Questions