4

Im trying to change style of only one element in map onClick, but it changes every element instead of one.

const maping = () => {
    return orders.map((item, index) => 
    <div className={styles.orders} key={index} onClick={() => {setSelectedAns("4px 3px 8px 0px rgba(1, 156, 48 , 0.3)")}} style={{boxShadow: selectedAns}}>
      <div className={styles.orderIDContainer}>
        <p className={styles.orderID}>{item.id}</p>
      </div>
      <div className={styles.description}>
        {item.description.map((food, index2) => { return (<p key={index2}> <font>{food.quantity}x</font> {food.foodItem.name} </p>) })}
      </div>
    </div>);
  };

Any ideas how to do it?

1
  • you need to separate your onclick handler. set the current selected element(use an identifier) and use it to apply the styles. like, if the selected element identifier is equal to current selected then apply styles. Commented Apr 20, 2022 at 23:07

3 Answers 3

6

You can use a ternary operator which chooses between the two values you want based on a state (named myStyle) of each particular iteration using the index i.

Note that this is assuming you want the style to change back and forth everytime you click.

Also note that it will only work if you aren't planning on dynamically reordering the items you are mapping through, otherwise you shouldn't use the index as the key but rather you should use an id.

  const [myStyle, setMyStyle] = useState({});
  const messages = [
    { content: "hi" },
    { content: "bonjour" },
    { content: "hola" }
  ];

  const handleClick = (id) => {
    setMyStyle(prevState => ({
      ...myStyle,
      [id]: !prevState[id]
    }))
  }
  
  return (
    <div>
      {messages.map((message, i) => (
        <div
          key={i}
          style={{
            boxShadow: myStyle[`${i}`] 
              ? "4px 3px 8px 0px rgba(1, 156, 48, 0.3)" 
              : "initial"
          }}
          onClick={() => handleClick(i)}
        >
          {message.content}
        </div>
      ))}
    </div>
  )

Edit hungry-platform-rtsedg

Sign up to request clarification or add additional context in comments.

Comments

0

const [componentSrc, setComponentSrc] = useState({});
const handleClick = (e, index) => {

     setComponentSrc(prev => ({
      ...componentSrc,
      [index]: !prev[index]
    }))
    
    setTimeout(() => {
      setComponentSrc(prev => ({
        ...componentSrc,
        [index]: !prev[index]
      }));
    }, 800);
    }

.map(e, index) => { ///// i am mapping the array here//
<div key={index}>
 <MyComponent
   className=""
   text=""
   src={componentSrc[`${index}`] ? "/images/a.svg" :  "/images/b.svg"}
   onClick={() => handleClick (e, index)}
 />
</div>
}

this is my code based on the previous comment. here MyComponent has b.svg as a default image source. when i click on it only the one i click shows a.svg for 800ms.

Comments

0

Use "Code Splitting". Create a seperate component for the item in the map and put the state inside this component. This creates a seperate instance for each item in the list and hence only applies for that particukar item.

Example:

<div>
      {messages.map((message, i) => (
        <Mycomponent pass props here />
      ))}
    </div>

const Button = ()=>{
const [Active,  setActive]=useState();
<div
          key={i}
          style={{
            boxShadow: Active 
              ? "4px 3px 8px 0px rgba(1, 156, 48, 0.3)" 
              : "initial"
          }}
          onClick={() => setActive(!Active)}
        >
          {message.content}
        </div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.