Home > Net >  How can I only open one popup at a time based on a map function in react?
How can I only open one popup at a time based on a map function in react?

Time:10-16

I am making a movie dashboard where users can search for movies and save them to their watch list. I am trying to have a popup containing the movie information that will appear if the user clicks on the image card for each movie. This is kind of working, but the problem is that the popup shows for every movie when I only click on one. My code is shown before. I haven't styled the popup yet, but the information looks right for each movie. Thanks for your help!

function Watchlist() {
  const { watchlist } = useContext(GlobalContext);
  const [modal, setModal] = useState(false);

  const toggleModal = () => {
      setModal(!modal);
  }

  return (
    <div className='watchlist'>
        <h1 className='title'>your watchlist</h1>

        {watchlist.length > 0 ? (
          <div className="movies">
            {watchlist.map((movie, index) => {
              if(index % 3 === 1) {
                return (
                  <div className="movie-wrapper">
                    <button name={movie.id} className="open-modal" onClick={toggleModal} key={movie.id}>
                      <Card img={`https://image.tmdb.org/t/p/w200${movie.poster_path}`} margin={30} key={movie.id}/>
                    </button>
                    {modal && (
                        <div className="movie-details">
                          <div className="modal-content">
                            <ResultCard movie={movie} key={movie.id}/>
                            <button className="close-modal" onClick={toggleModal}>X</button>
                          </div>
                      </div>
                    )}
                  </div>
                )
              } else {
                  <div className="movie-wrapper">
                  <button name={movie.id} className="open-modal" onClick={toggleModal} key={movie.id}>
                    <Card img={`https://image.tmdb.org/t/p/w200${movie.poster_path}`} margin={0} key={movie.id}/>
                  </button>
                  {modal && (
                    <div className="movie-details">
                      <div className="modal-content">
                        <ResultCard movie={movie} key={movie.id}/>
                        <button className="close-modal" onClick={toggleModal}>X</button>
                      </div>
                    </div>
                  )}
                </div>              
              }
            })}
          </div>
        ) : (
          <h2 className="no-movies">Add some movies!</h2>
        )}
    </div>
  )
}

CodePudding user response:

You need to keep the movie id you want to show the modal for:

 const [modal, setModal] = useState(false);
 const [selectedMovie, setSelectedMovie] = useState();

 const toggleModal = (movieId) => {
      setModal(!modal);
      setSelectedMovie(movieId)
 }


onClick={() => toggleModal(movie.id)}

and check the selected movie when rendering:

{modal && movie.id === selectedMovie && (...

CodePudding user response:

It's because you are rendering a modal for each movie in your list.

Take the modal out of the map function, and keep it as a child of the component where it does not rely on any condition, any mapped list, or anything else for it to be rendered. It's kind of a convention (at least among the codebases I've come across) to put modals at the end of the component JSX, as a direct child of the root element of the component.

Hide it by default, and only show it when a user clicks on a movie, passing the relevant info into the modal.

It might look something like this:

function Watchlist() {
  const { watchlist } = useContext(GlobalContext);
  const [modal, setModal] = useState(false);
  const [selectedMovie, setSelectedMovie] = useState({});

  const toggleModal = () => {
      setModal(!modal);
  }

  return (
    <div className='watchlist'>
        <h1 className='title'>your watchlist</h1>

        {watchlist.length > 0 ? (
          <div className="movies">
            {watchlist.map((movie, index) => {

              // Render the mapped list of JSX elements (movie cards?)
              // Potentially the `onClick` event would trigger the `setSelectedMovie` function in addition to setting `toggleModal`

            })}
          </div>
        ) : (
          <h2 className="no-movies">Add some movies!</h2>
        )}
        {modal && (
            <div className="movie-details">
                <div className="modal-content">
                    <ResultCard movie={selectedMovie} key={selectedMovie.id}/>
                    <button className="close-modal" onClick={toggleModal}>X</button>
                </div>
            </div>
        )}
    </div>
  )
}
  • Related