2

I am learning react and styled-components. I am stack trying to change color on every icon seperatly. Is there any solution for this?

Here is what I want to get:

https://i.stack.imgur.com/tsANO.jpg

Here is sample of my code:


import React, { useState } from "react";
import styled from "styled-components";
import {
  GiSteak,
  GiCheeseWedge,
  GiFlatfish,
  GiChickenOven,
  GiBroccoli,
} from "react-icons/gi";

const StyledDiv = styled.div`
  width: 300px;
  div {
    font-size: 30px;
    color: #828282;
    color: ${({ color }) => (color ? "green" : "lightGrey")};
  }
`;

const Selections = () => {
  const [color, setColor] = useState(false);
  return (
    <StyledDiv color={color} onClick={() => setColor(!color)}>
      <p>Select your options</p>
      <div>
        <GiBroccoli />
        <GiCheeseWedge />
        <GiSteak />
        <GiFlatfish />
        <GiChickenOven />
      </div>
    </StyledDiv>

  );
};
bigBadWolf
  • 100
  • 1
  • 9

2 Answers2

1

You are actually asking how to handle multiple states (hence not related to styled-components), as for now you have a single state for all icons, where you need a state per icon.

Here is one possible solution, managing a single array of icons states:

const Icon = styled.div`
  background-color: ${({ isColored }) => (isColored ? "green" : "lightGrey")};
`;

const StyledDiv = styled.div`
  width: 500px;
  .icon__box {
    display: flex;
    ${Icon} {
      border: 1px solid black;
      font-size: 30px;
      margin-right: 10px;
    }
  }
`;

const Icons = () => {
  const [areColored, setColor] = useState(Array(5).fill(false));
  console.log(areColored);
  return (
    <StyledDiv>
      <p>Select your options</p>
      <div className="icon__box">
        {areColored.map((isColored, index) => (
          <Icon
            key={index}
            isColored={isColored}
            onClick={() =>
              setColor((prev) => {
                const res = Object.assign([], prev, { [index]: !prev[index] });
                return res;
              })
            }
          >
            icon {index + 1}
          </Icon>
        ))}
      </div>
    </StyledDiv>
  );
};

Edit agitated-neumann-3xy0m

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
1

Hey you can make use of icon state which stores an object structure like {index:1,color:true}. Combining this with the nth-child(...) css selector will allow you to accomplish your task like so :-

import React, { useState } from "react";
import styled from "styled-components";

const StyledDiv = styled.div`
  width: 500px;
  .icon__box{
    display: flex;
    div {
      border: 1px solid black;
      font-size: 30px;
      margin-right: 10px;
      background-color:lightgrey
    }
    div:nth-child(${({icon})=>icon.index+1}){
      background-color: ${({ icon }) => (icon.color ? "green" : "lightGrey")};
    }
  }
`;

const Icons = () => {
  const [icon, setIcon] = useState({index:0,color:false});
  const [icons] = useState(['icon1','icon2','icon3','icon4','icon5']);
  const handleClick=(index)=>{
     let newIcon = {...icon}
     newIcon.color = index!==newIcon.index?true:!newIcon.color;
     newIcon.index = index;
     setIcon(newIcon);
  }

  return (
    <StyledDiv icon={icon}>
      <p>Select your options</p>
      <div className="icon__box">
        {icons.map((icon,index)=>(<div onClick={()=>handleClick(index)}>{icon}</div>))}
      </div>
    </StyledDiv>
  );
};

export default Icons;

Here's is the forked one :-

Edit jolly-wright-spum4

Note : Here I have made an icons state for the text content of each of your icon div. It will work the same way if you derive the above from props. So it's upto you.

Lakshya Thakur
  • 8,030
  • 1
  • 12
  • 39