Home > front end >  API endpoint inside useEffect not getting called when I navigate back - ReactJS
API endpoint inside useEffect not getting called when I navigate back - ReactJS

Time:10-07

I'm stuck in this ReactJS code and I need some help. Here I'm trying to create a movie app using TMDB api. This api has a search option (with this endpoint https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${search_term}) where I'm trying to get search movies. The search_term is what will be put in the input element. And based on that the API will search for that movie and then navigate to the path /search/movie-name. Everything works fine until now, when you search for different movies it also works every time. The problem I'm facing is when I go back. For example, I search for spiderman (the results get shown), after that I search for venom (the results get shown), after that search for x-man (also works). Now, when I try to go back the url changes just right to what was searched (from /search/x-man to /search/venom to /search/spiderman), but the api doesn't run, it only shows what was searched the latest, in this case x-man. I just want when I go back the searched results to also be shown based on which path we are.

Btw, I'm just a beginner at React, I'd really appreciate if I can get a solution for this problem.

Here you can find the two components, one is the page and the other one is a component which contains the input form.

The page:

import axios from "axios";
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import SearchMovie from '../components/SearchMovie'

const API_KEY = process.env.REACT_APP_API_KEY;
const IMAGE_BASE_URL = process.env.REACT_APP_HEADER_IMAGE_BASE_URL;

const Search = () => {
    const location = useLocation()
    const [search_term, set_search_term] = useState('spider');
    const [searchData, setSearchData] = useState(null)

    
    useEffect(() => {
        axios
            .get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${search_term}`)
            .then((res) => {
                setSearchData(res.data.results)
                navigate(`/search/${search_term}`)
            });
    }, [search_term]);

    return (
        <div className="mt-[68px] sm:mt-[72px] px-[1rem] sm:px-[2rem]">

            <SearchMovie search_term={search_term} set_search_term={set_search_term} />

            <h1 className="text-red-600 font-medium text-2xl sm:text-3xl text-center mt-[5rem]">Search Results...</h1>
            <div className="movies_grid_container mt-[2rem]">
                {searchData?.map((e) => {
                    return (
                        <div
                            key={e.id}
                            className="flex items-start justify-center flex-col row-carousel"
                        >
                            <img
                                className="w-full object-cover scale-95 hover:scale-90 active:outline outline-red-600 transition-transform rounded-md cursor-pointer select-none"
                                src={IMAGE_BASE_URL   e.poster_path}
                            />
                            <p className="text-red-600 mt-2 font-medium">
                                {e.title}
                            </p>
                        </div>
                    );
                })}
            </div>
        </div>
    );
};

export default Search;

The component that contains the search input:

import { useEffect, useState } from "react";
import { FaSearch } from "react-icons/fa";
import { useNavigate } from "react-router-dom";

const SearchMovie = ({ search_term, set_search_term }) => {
    const [input_val, set_input_val] = useState(search_term);
    const navigate = useNavigate()

    const handleSubmit = (e) => {
        e.preventDefault()
        set_search_term(input_val)
        // navigate(`/search/${search_term}`)
    }

    return (
        <div className="search-movie-section flex flex-col items-center justify-center max-w-[400px] my-10 mx-auto px-[1rem] sm:px-[2rem]">
            <h1 className="text-red-600 text-2xl font-[500]">Search Movie</h1>
            <form onSubmit={(e) => handleSubmit(e)} className="mt-4 flex gap-2 w-full">
                <input
                    type="text"
                    placeholder="Search Movie..."
                    className="placeholder:text-red-600/40 w-[87%] bg-transparent outline-none border-x-[1px] border-x-red-600/50 border-t-[1px] border-t-red-600/50 border-b-[2px] border-b-red-600 text-red-600 rounded-md p-2"
                    value={input_val}
                    onChange={(e) => set_input_val(e.target.value)}
                />
                <button className="w-[43px] h-[43px] p-2 border-2 border-red-600 grid place-items-center rounded-md hover:bg-red-600 active:scale-90">
                    <FaSearch className="text-red-600" />
                </button>
            </form>
        </div>
    );
};

export default SearchMovie;

CodePudding user response:

You are really close. Your source of truth for what to search for shouldn't be in state, but should instead be pulled from the URL. When it's in the state, there's no way for it to update when you navigate back (I mean, you're not really navigating back anyway, it's still the same page, just the URL updated)

So, your code should look something like this:

function Component(){
  const location = useLocation();
  const navigate = useNavigate()
  const [searchData, setSearchData] = useState(null)

  const search_term = location.pathname.split('/').pop();

  const set_search_term = React.useCallback((value) => {
    navigate(`/search/${value}`);
  }, [navigate]);

  React.useEffect(() => {
    if(search_term == null || search_term.length == 0)return;
    axios
      .get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${search_term}`)
      .then((res) => {
        setSearchData(res.data.results)
      });
  }, [search_term]);

  return (<>
    { /* stuff */ }
    <SearchMovie search_term={search_term} set_search_term={set_search_term} />
    { /* stuff */ }
  </>);
}

Since you're using react-router, you can actually get the search term by using proper routes and grabbing the param of the route. They call this dynamic segments: https://reactrouter.com/en/main/route/route#dynamic-segments

  • Related