0

I have a component where I use useEffect and inside call a function fetchLatest(). fetchLatest() is an action that makes a network request to get all movies that have been released during the current year. I am using Redux and in my store I have a state.movies.latest and each time I start the app for the first time the latest row of movies is accurate with no duplicated data. When I render a different component and go back to my home component where I do fetchLatest I then get duplicated data.

I figured out that my problem is because in my reducers I return something like...

return {
    ...state,
    latest: [...state, ...payload]
}

where payload is an array of objects (movies).

So I changed my reducers to...

return {
    ...state,
    latest: [...payload]
}

It works now and I don't get duplicated data but my concern is that I am not doing or understand this correctly. If I were to use useEffect and have my fetchLatest function in my useEffect hook and pass it as a dependency to my understanding my function fetchLatest will be recreated after every render cycle as a brand new object. Being that I already have in the store my latest array of movies I do not want to re-render a brand new object each time causing to add to my store in my state.movies.latest duplicated data. I at least believe this is what it is doing.

I decided that maybe I should be using useCallback to ensure that this brand new object of fetchLatest is not created unless its dependencies change.

So what I am not sure about is maybe I can have my updated reducer as I have it now and also useCallback to ensure no infinite loops are running for recreating my fetchLatest function each time as a new object when I re-build that component?

Also, I might not understand actually what useCallback does because I tried before using a useCallback and having my reducer as I originally had it and to my understanding if nothing changed then why is it that my fetchLatest runs when I rebuild the component. I only want my latest movies which I already have in the store when I first mount my component

my code for this component looks like this

const Latest = ({ fetchLatest, latest }) => {
  useEffect(() => {
    fetchLatest();
  }, [fetchLatest]);

  return (
    <div className='genre-temp'>
      <h3 className='text-light'>Latest</h3>
      <div className='movies-container d-flex'>
        {latest.map((movie) => (
          <Movie
            key={movie.id}
            movieId={movie.id}
            image={movie.image}
            title={movie.title}
            description={movie.description}
          />
        ))}
      </div>
    </div>
  );
};

and what I had tried to do initially was add this useCallback and call handleFetching in my useEffect and pass it in as a dependency.

const handleFetching = useCallback(() => {
    fetchLatest()
}, []);

However, useCallback in conjunction with useEffect while still having my reducer as such...

return {
    ...state,
    latest: [...state, ...payload]
}

still would run each time the component rebuilt.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Michael Torres
  • 512
  • 3
  • 21

1 Answers1

0

Try to add a request state to your state object. Then, you'll be able to check if the list is fetched. Usually, I use four states for requests: init, request, success, failure. Here is a rough example of a request:

if (requestState !== 'request' && requestState !== 'success') {
    try {
        setRequestState('request');
        const response = await fetchList();
        setList(response);
        setRequestState('success');
    catch (e) {
        setRequestState('failure');
        log(e);
    }
}

If you have a request state, you'll always know when you need to request the data, show a spinner, show an error message, etc. Hopefully, you got the idea.

P.S. In your code, it's supposed to be:

{
    ...state,
    latest: [...payload]
}

latest: [...state.latest, ...payload] won't work as you expect. latest: [...state, ...payload] looks wrong.