Home > Software engineering >  How to use useState in loop?
How to use useState in loop?

Time:10-05

I'm trying develop a little app in which on you can select multiple music album, using Next.js.

I display my albums like the image below, and I would like to add a check mark when clicked and hide it when clicked again.

display album

My code looks like that :

import Image from "next/image";
import {Card,CardActionArea} from "@mui/material";
import { container, card } from "../styles/forms.module.css";
import album from "../public/album.json"


export default function Album() {
    const albumList = {} ;

    function addAlbum(albumId, image){
        if ( !(albumId in albumList) ){
            albumList[albumId] = true;
            //display check on image
        }
        else{
            delete albumList[albumId]
            //hide check on image
        }
        console.log(albumList)
    }

    return (
        <div className={container}>
            {Object.keys(album.albums.items).map((image) => (
                <Card className={card}>
                    <CardActionArea onClick={() => addAlbum(album.albums.items[image].id)}>
                        <Image alt={album.albums.items[image].artists[0].name} width="100%" height="100%" src={album.albums.items[image].images[1].url} />
                    </CardActionArea>
                    </Card>
            ))}
        </div>
    );
}

I know I should use useState to do so, but how can I use it for each one of my albums?

Sorry if it's a dumb question, I'm new with Hook stuff.

CodePudding user response:

I think there are a few ways to go about this, but here is a way to explain the useState in a way that fits the question. CodeSandbox

For simplicity I made a Card component that knowns if it has been clicked or not and determines wither or not it should show the checkmark. Then if that component is clicked again a clickhandler from the parent is fired. This clickhandle moves the Card into a different state array to be handled.

The main Component:

export default function App() {
  const [unselectedCards, setUnselectedCards] = useState([
    "Car",
    "Truck",
    "Van",
    "Scooter"
  ]);
  const [selectedCards, setSelectedCards] = useState([]);

  const addCard = (title) => {
    const temp = unselectedCards;
    const index = temp.indexOf(title);
    temp.splice(index, 1);
    setUnselectedCards(temp);
    setSelectedCards([...selectedCards, title]);
  };

  const removeCard = (title) => {
    console.log("title", title);
    const temp = selectedCards;
    const index = temp.indexOf(title);
    temp.splice(index, 1);
    setSelectedCards(temp);
    setUnselectedCards([...unselectedCards, title]);
  };
  return (
    <div className="App">
      <h1>Current Cards</h1>
      <div style={{ display: "flex", columnGap: "12px" }}>
        {unselectedCards.map((title) => (
          <Card title={title} onClickHandler={addCard} key={title} />
        ))}
      </div>
      <h1>Selected Cards</h1>
      <div style={{ display: "flex", columnGap: "12px" }}>
        {selectedCards.map((title) => (
          <Card title={title} onClickHandler={removeCard} key={title} />
        ))}
      </div>
    </div>
  );
}

The Card Component

export const Card = ({ onClickHandler, title }) => {
  const [checked, setChecked] = useState(false);

  const handleClickEvent = (onClickHandler, title, checked) => {
    if (checked) {
      onClickHandler(title);
    } else {
      setChecked(true);
    }
  };

  return (
    <div
      style={{
        width: "200px",
        height: "250px",
        background: "blue",
        position: "relative"
      }}
      onClick={() => handleClickEvent(onClickHandler, title, checked)}
    >
      {checked ? (
        <div
          id="checkmark"
          style={{ position: "absolute", left: "5px", top: "5px" }}
        ></div>
      ) : null}
      <h3>{title}</h3>
    </div>
  );
};

I tried to make the useState actions as simple as possible with just a string array to help you see how it is used and then you can apply it to your own system.

CodePudding user response:

You do not need to have a state for each album, you just need to set albumList as a state:

const [albumList, setAlbumList] = setState({});

function addAlbum(albumId, image) {
  const newList = {...albumList};

  if(!(albumId in albumList)) {
    newList[albumId] = true;
  } else {
    delete albumList[albumId]
  }

  setAlbumList(newList);
}

And then in your loop you can make a condition to display the check mark or not by checking if the id is in albumList.

  • Related