Daniels0n
Daniels0n

Reputation: 93

useEffect not triggering when clicking on dependency

I got a button that triggers data fetching from fetchDataHandler, that works with useEffect, it fetch data on page load also. But then when I add addMovieHandler as dependency to useEffect to auto-fetch data when I add another movie, it just doesn't work. So I figured out I may be using it wrong, but cant figure it out.

const App = () => {
  const [movies, setMovies] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  const fetchDataHandler = useCallback(async () => {
    setIsLoading(true);
    const respone = await fetch(
      "https://react-http-3af47-default-rtdb.firebaseio.com/movies.json"
    );
    if (!respone.ok) {
      setIsLoading(false);
      throw new Error("Something went wrong!");
    }
    const data = await respone.json();
    const movieList = [];

    for (const key in data) {
      movieList.push({
        id: key,
        title: data[key].title,
        text: data[key].text,
      });
    }

    setMovies(movieList);
    setIsLoading(false);
  }, []);

  const addMovieHandler = useCallback(async (movie) => {
    const respone = await fetch(
      "https://react-http-3af47-default-rtdb.firebaseio.com/movies.json",
      {
        method: "POST",
        body: JSON.stringify(movie),
        headers: {
          "Content-Type": "application/json",
        },
      }
    );
    const data = await respone.json();
    console.log(data);
  }, []);

  useEffect(() => {
    fetchDataHandler();
  }, [fetchDataHandler, addMovieHandler]);

  let content = <p>no movies found</p>;

  if (movies.length > 0) {
    content = <MovieList movies={movies} />;
  }
  if (isLoading) {
    content = <p>loading...</p>;
  }

  return (
    <React.Fragment>
      <UserInput onAddMovie={addMovieHandler}></UserInput>
      <Card>
        <button onClick={fetchDataHandler}>Fetch movies</button>
      </Card>
      {content}
    </React.Fragment>
  );
};

export default App;

Upvotes: 1

Views: 858

Answers (2)

zhulien
zhulien

Reputation: 5715

Your useEffect is not triggering on addMovieHandler reference change because you're using useCallback with no dependencies ([] second parameter) to memoize it. This means the addMovieHandler reference won't change at all between rerenders but you're basing your useEffect calls on it doing so. In simple words - useEffect won't rerun if the provided dependencies have the same values (which is the case in your solution).

Same goes for fetchDataHandler.

In any case, this is not a good solution to the problem. You should be better-off updating your movies array on successful movie addition in the addMovieHandler itself. Same goes for other operations like delete and update.

Upvotes: 1

zmmsayeed
zmmsayeed

Reputation: 111

Try this instead:

Remove addMovieHandler from useEffect. When you hit addMovieHandler it is fetching the data anyway. So what you could to is set the movies with the latest response in the handler itself like so:

setMovies(...data)

This will replace the movie list with the newly fetched movie list. If you want to add on the old list you could do this:

setMovies([...movies, data])

This is concatenate the new fetched list to the already existing movie list and update the variable and re-render the movie list section as well.

Upvotes: 1

Related Questions