Search code examples
reactjsuse-stateconditional-operatortogglebuttonspread

React: how to use spread operator in a function that toggles states?


I made an example of my question here: EXAMPLE

I'm mapping an array of objects that have a button that toggles on click, but when clicking on the button every object is changed.

This is the code

export default function App() {
  const [toggleButton, setToggleButton] = useState(true);

  // SHOW AND HIDE FUNCTION
  const handleClick = () => {
    setToggleButton(!toggleButton);
  };

  return (
    <div className="App">
      <h1>SONGS</h1>
      <div className="container">
        {/* MAPPING THE ARRAY */}
        {songs.map((song) => {
          return (
            <div className="song-container" key={song.id}>
              <h4>{song.name}</h4>
              {/* ON CLICK EVENT: SHOW AND HIDE BUTTONS */}
              {toggleButton ? (
                <button onClick={handleClick}>PLAY</button>
              ) : (
                <button onClick={handleClick}>STOP</button>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

I know I should be using spread operator, but I couldn't get it work as I spected.

Help please!


Solution

  • Of course every object will change because you need to keep track of toggled state for each button. Here is one way to do it:

    import { useState } from "react";
    import "./styles.css";
    
    const songs = [
      {
        name: "Song A",
        id: "s1"
      },
      {
        name: "Song B",
        id: "s2"
      },
      {
        name: "Song C",
        id: "s3"
      }
    ];
    
    export default function App() {
      const [toggled, setToggled] = useState([]);
    
      const handleClick = (id) => {
        setToggled(
          toggled.indexOf(id) === -1
            ? [...toggled, id]
            : toggled.filter((x) => x !== id)
        );
      };
      return (
        <div className="App">
          <h1>SONGS</h1>
          <div className="container">
            {songs.map((song) => {
              return (
                <div className="song-container" key={song.id}>
                  <h4>{song.name}</h4>
                  {toggled.indexOf(song.id) === -1 ? (
                    <button onClick={() => handleClick(song.id)}>PLAY</button>
                  ) : (
                    <button onClick={() => handleClick(song.id)}>STOP</button>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      );
    }
    

    There are many ways to do it. Here, if an id is in the array it means that button was toggled. You can also keep ids of toggled buttons in object for faster lookup.