React - useState Hook with Context Api - can't copy a state array to another state array

1.2k views Asked by At

I am importing an array into "restMovies" and it's working fine - all the data is in place in "restmovies" and is delievered to the rest of the app via the context provider.

However, I need to make changes in the array so I'm trying to copy the "restMovies" array to the "movies" array that will be the array that's being delievered to the rest of the app.

But for some reason I can't "setMovies", the movies array stays empty.

Help?

export const MoviesContext = createContext();
export const MoviesContextProvider = (props) =>
{
        const [restMovies, setRestMovies] = useState([]);
        const [movies, setMovies] = useState([])

        useEffect(() =>
        {
            axios.get("https://api.tvmaze.com/shows").then(resp => setRestMovies(resp.data));
        }, [restMovies.values])


        useEffect(() =>
        {
            restMovies.map(item => 
            {
                setMovies([...movies, item])
            })

        }, [movies.values, restMovies.values])
     
        return (
          <MoviesContext.Provider value = {[movies]}>
             {props.children}
          </MoviesContext.Provider>)
}
3

There are 3 answers

1
Drew Reese On BEST ANSWER

Issue

movies.values, and restMovies.values are undefined always, so the dependency values never change and the effect callback is never re-triggered (from the initial triggering when mounted). Mapping arrays is also for returning new arrays, not side-effects like pushing into other arrays, etc...

Solution

Run the second effect with only restMovies array as a dependency (don't cause infinite looping), and use a functional state update to correctly spread (copy) current movies and restMovies state arrays into a new array reference. I assume you likely also only wanted the first effect to run once when the component mounts in order to do the data fetch, so remove all dependencies for the first effect.

useEffect(() => {
    axios.get("https://api.tvmaze.com/shows").then(resp => setRestMovies(resp.data));
}, [])

useEffect(() => {
  setMovies(movies => [...movies, ...restMovies])
}, [restMovies])
0
Seim2k20 On

is it possible that you need to create a copy of "restMovies" outside the Movies setter function and THEN set this the new movies state ? in your second useEffect - hook like so:

const moviesCopy = [...movies,item];
setMovies(moviesCopy);
0
deowk On

Be careful not to update the same values that are triggering your effects to run since this will re-trigger each time your setState functions run.

export const MoviesContext = createContext();
export const MoviesContextProvider = (props) => {
  const [restMovies, setRestMovies] = useState([]);
  const [movies, setMovies] = useState([]);

  // This will execute once when the component mounts
  useEffect(() => {
    axios
      .get("https://api.tvmaze.com/shows")
      .then((resp) => setRestMovies(resp.data));
  }, []);

  // This will run when restMovies updates
  useEffect(() => {
    restMovies.map((item) => {
      setMovies([...movies, item]);
    });
  }, [restMovies]);

  return (
    <MoviesContext.Provider value={[movies]}>
      {props.children}
    </MoviesContext.Provider>
  );
};