skip to Main Content

I am working on an API project using React-Router-DOM for navigation and when the initial search results are called I have an array of 8 movies that are shown based on a keyword from the user’s search query. Once the user has chosen the specific movie they are looking for by clicking the "SEE MORE" button attached to the movie card it is supposed to redirect to another page that shows specific details about the selected movie, but instead I keep getting uncaught runtime errors that is saying I have an invalid hook call but I’m not sure where I went wrong.

Below is the code that is first used after the original search query :

import axios from "axios";
import React, { useEffect, useState } from "react";
import Nav from "../ui/Nav";
import { Navigate, useParams } from "react-router-dom";

const API_URL = "http://www.omdbapi.com/?apikey=cca6a59";

function Movies() {
  const [movies, setMovies] = useState([]);
  const params = useParams();
  const title = params.id;
  const [loading, setLoading] = useState(true);

  async function onSearch() {
    await fetchMovies();

    Navigate(`/moviecard/${movies}`);

    fetchMovies(movies)
  }

  async function fetchMovies(title) {
    const { data } = await axios.get(`${API_URL}&s=${title}`);
    setMovies(data.Search);
    setLoading(false);
  }

  useEffect(() => {
    fetchMovies(title);
  }, [title]);

  return (
    <>
      <Nav />
      <div className="movie__row">
        <div className="movie__wrapper">
          {loading
            ? new Array(8).fill(0).map((_, index) => (
                <div className="movie" key={index}>
                  <div className="movie__img--skeleton">
                    <div className="movie__title--skeleton">
                      <div className="movie__year--skeleton"></div>
                    </div>
                  </div>
                </div>
              ))
            : movies.map((movie) => (
                <div className="movie" key={movie.id}>
                  <div className="movie__img">
                    <img src={`${movie.Poster}`} alt="poster" />
                    <div className="movie__content">
                      <h1>{movie.Title}</h1>
                      <h1>{movie.Year}</h1>
                      <p onClick={() => onSearch()}>SEE MORE</p>
                    </div>
                  </div>
                </div>
              ))}
        </div>
      </div>
    </>
  );
}

export default Movies;

This is the code after the user selects "SEE MORE" after selecting the specific movie:

import { useParams } from "react-router-dom";
import Nav from "../ui/Nav";
import { useEffect, useState } from "react";
import axios from "axios";

const API_URL = "http://www.omdbapi.com/?apikey=cca6a59";

function Moviecard() {
  const params = useParams();
  const imdbID = params.id;
  const [desc, setDesc] = useState([]);
  const [loading, setLoading] = useState(true);

  async function fecthDesc() {
    const { data } = await axios.get(`${API_URL}&i=${imdbID}`);
    setDesc(data.Search);
    setLoading(false);
  }

  useEffect(() => {
    fecthDesc();
  }, []);

  return (
    <>
      <Nav />

      {loading
        ? new Array(1).fill(0).map((_, movie) => (
            <div className="movie__img--wrapper">
              <h1>${movie.Title}</h1>
              <img src={`${movie.Poster}`} alt="" />
            </div>
          ))
        : desc.map((movie) => (
            <div className="movie__info--wrapper" key={imdbID}>
              <h3>
                <span className="red">Released: </span>${movie.Released}
              </h3>
              <h3>
                <span className="red">Actors: </span>${movie.Actors}
              </h3>
              <h3>
                <span className="red">Genre: </span>${movie.Genre}
              </h3>
              <h3>
                <span className="red">Director: </span>${movie.Director}
              </h3>
              <h3>
                <span className="red">Writer: </span>${movie.Writer}
              </h3>
              <h3>
                <span className="red">Language: </span>${movie.Language}
              </h3>
              <h3>
                <span className="red">Plot: </span>${movie.Plot}
              </h3>
            </div>
          ))}
    </>
  );
}

export default Moviecard;

I’m sure it is something small that I’m just forgetting but I don’t know how to attack this problem.

2

Answers


  1. The code is directly calling, e.g invoking, the Navigate component instead of passing it as JSX to be rendered by React. This isn’t what you want to do here though.

    • Import the useNavigate hook from React-Router-DOM and issue an imperative navigation action.
    • Pass the currently mapped movie id or unique identifier to the search callback to handle issuing the navigation action to the appropriate route path.
    • Remove the fetchMovies call from the callback, the movies were already fetched when the component mounted.

    Example:

    import { useNavigate, useParams } from "react-router-dom";
    
    function Movies() {
      const navigate = useNavigate();
      const { id: title } = useParams();
    
      const [movies, setMovies] = useState([]);
      const [loading, setLoading] = useState(true);
    
      function onSearch(movieId) {
        navigate(`/moviecard/${movieId}`);
      }
    
      useEffect(() => {
        async function fetchMovies(title) {
          try {
            const { data } = await axios.get(`${API_URL}&s=${title}`);
            setMovies(data.Search);
          } catch(error) {
            // catch and handle/ignore
          } finally {
            setLoading(false);
          }
        }
    
        fetchMovies(title);
      }, [title]);
    
      return (
        <>
          <Nav />
          <div className="movie__row">
            <div className="movie__wrapper">
              {loading
                ? new Array(8).fill(0).map((_, index) => (
                    <div className="movie" key={index}>
                      <div className="movie__img--skeleton">
                        <div className="movie__title--skeleton">
                          <div className="movie__year--skeleton"></div>
                        </div>
                      </div>
                    </div>
                  ))
                : movies.map((movie) => (
                    <div className="movie" key={movie.id}>
                      <div className="movie__img">
                        <img src={`${movie.Poster}`} alt="poster" />
                        <div className="movie__content">
                          <h1>{movie.Title}</h1>
                          <h1>{movie.Year}</h1>
                          <p onClick={() => onSearch(movie.id)}>SEE MORE</p>
                        </div>
                      </div>
                    </div>
                  ))}
            </div>
          </div>
        </>
      );
    }
    
    Login or Signup to reply.
  2. async function onSearch() {
    await fetchMovies();

    This fetchMovies() looks suspicious, you didn`t provide parameter. It is intended?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search