Home > Net >  How to make only the clicked icon true using useState, react and typescript?
How to make only the clicked icon true using useState, react and typescript?

Time:11-10

After clicking on the FavoriteIcon inside the ExamplesCard all the icons turn red instead of just one icon. I'm using useState to change icon state value between true and false and style to change icon color.

Componenet ExamplesCard.tsx

import * as React from "react";
import {
  Box,
  Card,
  CardActionArea,
  CardActions,
  CardContent,
  CardMedia,
  Grid,
  Typography
} from "@mui/material";
import FavoriteIcon from "@mui/icons-material/Favorite";
import { ButtonProps } from "./ButtonProps";
import data from "./data/data.json";
import { useState } from "react";

export default function ExemplesCard() {
  const [iconFavorite, setIconFavorite] = useState(false);
  const ChangeColorFavorite = () => {
    setIconFavorite(!iconFavorite);
  };
  return (
    <Box sx={{ flexGrow: 1, margin: "0 2rem" }}>
      <Grid
        container
        justifyContent="center"
        spacing={{ xs: 8, sm: 8, md: 8, lg: 8, xl: 8 }}
        className="GRID1"
      >
        {data.map((item) => {
          return (
            <Grid
              item
              xs={12}
              sm={6}
              md={4}
              lg={3}
              xl={2}
              sx={{
                display: "flex",
                justifyContent: "center",
                marginTop: "1rem"
              }}
            >
              <Card
                key={item.id}
                sx={{
                  p: "1rem",
                  boxShadow: 4,
                  maxWidth: {
                    xs: "250px",
                    sm: "250px",
                    md: "280px",
                    lg: "300px",
                    xl: "300px"
                  }
                }}
              >
                <CardActionArea>
                  <CardMedia component="img" height="140" image={item.img} />
                  <CardContent>
                    <Typography gutterBottom variant="h5" component="div">
                      {item.title}
                    </Typography>
                    <Typography variant="body2" color="text.secondary">
                      Lizards are a widespread group of squamate reptiles, with
                      over 6,000 species, ranging across all continents except
                      Antarctica
                    </Typography>
                  </CardContent>
                </CardActionArea>
                <CardActions>
                  <ButtonProps
                    size="small"
                    endIcon={
                      <FavoriteIcon
                        onClick={ChangeColorFavorite}
                        style={{ color: iconFavorite ? "red" : "white" }}
                      />
                    }
                  >
                    Favorite
                  </ButtonProps>
                </CardActions>
              </Card>
            </Grid>
          );
        })}
      </Grid>
    </Box>
  );
}

I used the useState to store the current value of the icon (true or false), however, it was not enough to leave only the icon clicked in red. When clicking on the FavoriteIcon I expected that only the icon clicked would turn red.

Code link in codesanbox

CodePudding user response:

Try doing this instead. It might work.

import * as React from "react";
import {
  Box,
  Card,
  CardActionArea,
  CardActions,
  CardContent,
  CardMedia,
  Grid,
  Typography
} from "@mui/material";
import FavoriteIcon from "@mui/icons-material/Favorite";
import { ButtonProps } from "./ButtonProps";
import data from "./data/data.json";
import { useState } from "react";

export default function ExemplesCard() {
  const [iconFavorite, setIconFavorite] = useState(false);

  return (
    <Box sx={{ flexGrow: 1, margin: "0 2rem" }}>
      <Grid
        container
        justifyContent="center"
        spacing={{ xs: 8, sm: 8, md: 8, lg: 8, xl: 8 }}
        className="GRID1"
      >
        {data.map((item) => {
          return (
            <Grid
              item
              xs={12}
              sm={6}
              md={4}
              lg={3}
              xl={2}
              sx={{
                display: "flex",
                justifyContent: "center",
                marginTop: "1rem"
              }}
            >
              <Card
                key={item.id}
                sx={{
                  p: "1rem",
                  boxShadow: 4,
                  maxWidth: {
                    xs: "250px",
                    sm: "250px",
                    md: "280px",
                    lg: "300px",
                    xl: "300px"
                  }
                }}
              >
                <CardActionArea>
                  <CardMedia component="img" height="140" image={item.img} />
                  <CardContent>
                    <Typography gutterBottom variant="h5" component="div">
                      {item.title}
                    </Typography>
                    <Typography variant="body2" color="text.secondary">
                      Lizards are a widespread group of squamate reptiles, with
                      over 6,000 species, ranging across all continents except
                      Antarctica
                    </Typography>
                  </CardContent>
                </CardActionArea>
                <CardActions>
                  <ButtonProps
                    size="small"
                    endIcon={
                      <FavoriteIcon
                        onClick={() => setIconFavorite(true)}
                        style={iconFavorite ? {color: "red"} : {color: "black"}}
                      />
                    }
                  >
                    Favorite
                  </ButtonProps>
                </CardActions>
              </Card>
            </Grid>
          );
        })}
      </Grid>
    </Box>
  );
}

CodePudding user response:

your are using one state iconFavorite of type boolean where you actually want to use amount of the data array entries of type boolean[]. Every icon needs its own color state. This code works.

import * as React from "react";
import {
  Box,
  Card,
  CardActionArea,
  CardActions,
  CardContent,
  CardMedia,
  Grid,
  Typography
} from "@mui/material";
import FavoriteIcon from "@mui/icons-material/Favorite";
import { ButtonProps } from "./ButtonProps";
import data from "./data/data.json";
import { useState } from "react";

export default function ExemplesCard() {
  const [iconFavorite, setIconFavorite] = useState([ ...Array(data.length).keys() ].map( i => false));
  const ChangeColorFavorite = (idx: number) => {
    const newIconFavorite = iconFavorite.slice();
    newIconFavorite[idx] = !iconFavorite[idx];
    setIconFavorite(newIconFavorite);
  };
  return (
    <Box sx={{ flexGrow: 1, margin: "0 2rem" }}>
      <Grid
        container
        justifyContent="center"
        spacing={{ xs: 8, sm: 8, md: 8, lg: 8, xl: 8 }}
        className="GRID1"
      >
        {data.map((item,idx) => {
          return (
            <Grid
              item
              xs={12}
              sm={6}
              md={4}
              lg={3}
              xl={2}
              sx={{
                display: "flex",
                justifyContent: "center",
                marginTop: "1rem"
              }}
            >
              <Card
                key={item.id}
                sx={{
                  p: "1rem",
                  boxShadow: 4,
                  maxWidth: {
                    xs: "250px",
                    sm: "250px",
                    md: "280px",
                    lg: "300px",
                    xl: "300px"
                  }
                }}
              >
                <CardActionArea>
                  <CardMedia component="img" height="140" image={item.img} />
                  <CardContent>
                    <Typography gutterBottom variant="h5" component="div">
                      {item.title}
                    </Typography>
                    <Typography variant="body2" color="text.secondary">
                      Lizards are a widespread group of squamate reptiles, with
                      over 6,000 species, ranging across all continents except
                      Antarctica
                    </Typography>
                  </CardContent>
                </CardActionArea>
                <CardActions>
                  <ButtonProps
                    size="small"
                    endIcon={
                      <FavoriteIcon
                        onClick={() => ChangeColorFavorite(idx)}
                        style={{ color: iconFavorite[idx] ? "red" : "white" }}
                      />
                    }
                  >
                    Favorite
                  </ButtonProps>
                </CardActions>
              </Card>
            </Grid>
          );
        })}
      </Grid>
    </Box>
  );
}

  • Related