1

I am trying to click on one card of a dynamically created list using map(). I want to click on one card from the array and add a class to it, while at the same time deselecting the other card that was previously clicked. How can I accomplish this? This is what I have so far:

const CardList = () => {
  return (
    <div className='card-list'>
      {CardData.map(({ id, ...otherData }) => (
        <Card key={id} {...otherData} />
      ))}
    </div>
  );
};

export default CardList;

const Card = ({
  headline,
  time,
  views,
  thumbImg,
  trainerImg,
  workouts,
  id
}) => {
  const [isSelected, setIsSelected] = useState(false);
  const [clickId, setClickId] = useState('');

  function handleClick(id) {
    setIsSelected(!isSelected);
    setClickId(id);
  }

  return (
    <div
      className={`card ${isSelected && clickId === id ? 'clicked' : ''}`}
      onClick={() => handleClick(id)}
    >
      <div className='thumbnail-div'>
        <img className='thumbnail-img' src={thumbImg} alt='video' />
        {workouts ? (
          <div className='workout-overlay'>
            <p>{workouts}</p>
            <p className='workouts'>workouts</p>
          </div>
        ) : null}
      </div>

      <div className='card-info'>
        <div className='card-headline'>
          <p>{headline}</p>
          <img src={trainerImg} alt='trainer' />
        </div>
        {time && views ? (
          <div className='trainer-data'>
            <span>
              <i className='glyphicon glyphicon-time'></i>
              {time}
            </span>
            <span>
              <i className='glyphicon glyphicon-eye-open'></i>
              {views}
            </span>
          </div>
        ) : null}
      </div>
    </div>
  );
};

export default Card;
2
  • also include snippet of where you are using Card. I mean the map function. Commented Feb 26, 2021 at 16:58
  • Ok, I just added it. Commented Feb 26, 2021 at 17:02

2 Answers 2

1

The parent component should control what card is clicked. Add className property to card component:

const Card = ({
    //...
    className,
    onClick    
}) => {
     //...    
     return (
        <div
         className={`card ${className}`}
         onClick={() => onClick(id)}
       >...</div>
    ) 
}

In parent component pass the className 'clicked' and add the onClick callback to set the selected card:

const CardList = () => {
  const [isSelected, setIsSelected] = useState(null);
  
  const handleClick = (id) => {
    setIsSelected(id);
  }
  
  return (
    <div className='card-list'>
      {CardData.map(({ id, ...otherData }) => (
        <Card key={id} className={isSelected===id && 'clicked'} onClick ={handleClick} {...otherData} />
      ))}
    </div>
  );
};
Sign up to request clarification or add additional context in comments.

1 Comment

Sweet this worked. In you code for the child component I added onClick={() => onClick(id)}. Instead of onClick={() => handleClick(id)}.
0

You can do something like this.

First you don't have to set state to each card. Instead Lift state Up.

You define which card is selected in parent so you can pass that to children and add classes if current selected is matching that children.


const CardList = () => {

const [isSelected, setIsSelected] = useState();

const handleCardClick = (id) => {
   setIsSelected(id);
 }

 return (
   <div className='card-list'>
     {CardData.map(({ id, ...otherData }) => (
       <Card key={id} {...otherData} handleClick={handleCardClick} isSelected={isSelected}/>
     ))}
   </div>
 );
};

export default CardList;
const Card = ({
  headline,
  time,
  views,
  thumbImg,
  trainerImg,
  workouts,
  id,
  isSelected,
  handleClick
}) => {

  return (
    <div
      className={`card ${isSelected === id ? 'clicked' : ''}`}
      onClick={() => handleClick(id)}
    >
      <div className='thumbnail-div'>
        <img className='thumbnail-img' src={thumbImg} alt='video' />
        {workouts ? (
          <div className='workout-overlay'>
            <p>{workouts}</p>
            <p className='workouts'>workouts</p>
          </div>
        ) : null}
      </div>

      <div className='card-info'>
        <div className='card-headline'>
          <p>{headline}</p>
          <img src={trainerImg} alt='trainer' />
        </div>
        {time && views ? (
          <div className='trainer-data'>
            <span>
              <i className='glyphicon glyphicon-time'></i>
              {time}
            </span>
            <span>
              <i className='glyphicon glyphicon-eye-open'></i>
              {views}
            </span>
          </div>
        ) : null}
      </div>
    </div>
  );
};

export default Card;

2 Comments

When I do this it adds the 'clicked' class to all the items in the array.
Did you mean to set isSelected={isSelected} instead of isSelected{id}. I tried this and then set useState(''') to an empty string. When I do this it adds the class to all items when I click on one.

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.