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?
CodePudding user response:
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>
);
CodePudding user response:
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>
)
}