'How to push new value in array useState in react

I want when I click on a card of the movie name, this movie name is pushed in useSate. The problem is when I click on the same card, data is saved in the useState as an array. But when I click on a different card previous value of the useState is deleted.

Here is my code. movie is a single object.

const MovieCard = ({ movie }) => {
    const [firstCat, setFirstCat] = React.useState([]);
    



    const movieList = (movie) => {
        setFirstCat([...firstCat, movie]);
    }

    console.log(firstCat);

    return (
        <div className='mt-3 cursor-pointer' onClick={() => movieList(movie)}>
            <div className="max-w-sm bg-white rounded-lg border border-gray-200 shadow-md">

                <img className="rounded-t-lg" src="/docs/images/blog/image-1.jpg" alt="" />

                <div className="p-5">

                    <h5 className="mb-2 text-xl font-bold tracking-tight text-gray-900">{movie.Title}</h5>

                </div>
            </div>
        </div>
    );
};

export default MovieCard;

when clicking on the same card, useState is like this, clicking on the same card

When clicking on the different card enter image description here



Solution 1:[1]

Your problem is you scoped your states only in MovieCard which means each MovieCard will have different states.

You need to add states on the upper/parent component of MovieCard. Let's assume you have MovieList contains all MovieCard

const MovieList = () => {
   const [firstCat, setFirstCat] = React.useState([]);

   console.log(firstCat);

   return <div>
      <MovieCard movie={movie} setFirstCat={setFirstCat}/>
   <div/>
}

And modify your MovieCard like below

const MovieCard = ({ movie, setFirstCat }) => {

    const movieList = (movie) => {
        setFirstCat([...firstCat, movie]);
    }

    return (
        <div className='mt-3 cursor-pointer' onClick={() => movieList(movie)}>
            <div className="max-w-sm bg-white rounded-lg border border-gray-200 shadow-md">

                <img className="rounded-t-lg" src="/docs/images/blog/image-1.jpg" alt="" />

                <div className="p-5">

                    <h5 className="mb-2 text-xl font-bold tracking-tight text-gray-900">{movie.Title}</h5>

                </div>
            </div>
        </div>
    );
};

export default MovieCard;

This technique is called state lifting

Solution 2:[2]

Looks like each MovieCard component has its own firstCat state. When you click another movie card you are updating that card's state.

You likely need to lift state up and store the firstCat state in the common parent component so all MovieCard components can reference the same single state.

Example:

const Movies = () => {
  const [movies, setMovies] = React.useState([.......]);
  const [firstCat, setFirstCat] = React.useState([]);

  const addMovie = (movie) => {
    setFirstCat(firstCat => [...firstCat, movie]);
  }

  return movies.map(movie => (
    <MovieCard
      key={movie.id}
      movie={movie}
      addMovie={() => addMovie(movie)}
    />
  ));
}

...

const MovieCard = ({ movie, addMovie }) => {
  return (
    <div className='....' onClick={addMovie}>
      <div className="....">
        <img
          className="...." 
          src="...."
          alt=""
        />
        <div className="p-5">
          <h5 className="....">
            {movie.Title}
          </h5>
        </div>
      </div>
    </div>
  );
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nick Vu
Solution 2 Drew Reese