Home > Mobile >  React toggle style on mapped element on click
React toggle style on mapped element on click

Time:11-14

A React newbie here. A very simple thing that I just can't seem to get right.

I want to change the class on the clicked element from my map, not all of them.

E.g if I click on element with an id of 1, it should have a class of 'active-color'. If I then click on element with an id of 2, that element should then take class 'active-color' and id 1 should not have this class anymore.

I've tried many different solutions, but can't seem to get this right. It doesn't seem to like me using the hook in my handler function here and returns an error: TypeError: setActive is not a function.

Any ideas or other solutions will be much appreciated!


const ListItems =  () => {

  const { isActive, setActive} = useState(false)
  const { item, dispatch } = useContext(AppContext);

  const handler = (event) => {
        dispatch({
            type: 'SET_ITEM',
            payload: event.currentTarget.dataset.index,
        });
    setActive(!isActive);
    };


  return (
    <div className="container">
      <h2 className="smallheader">Please choose one</h2>
      <div className="flex flex-center">
      {ITEMS.map((item) =>
        <div className={isActive ? 'active-color' : 'card'} data-index={item.value} key={item.id} onClick={handler}>
        <span
          className="card-header">{item.name}</span>
        <span className="card-description">{item.description}</span>
        </div>
      )}
      </div>
    </div>
  )
};
 
 
export default ListItems;

CodePudding user response:

Array destructuration requires square brackets:

Instead of:

const { isActive, setActive} = useState(false)
const { item, dispatch } = useContext(AppContext);

Write:

const [ isActive, setActive] = useState(false)
const [ item, dispatch ] = useContext(AppContext);

CodePudding user response:

Your first issue is that useState() returns an array, not an object. That means that when you destructure isActive and setIsActive you need to use array destructuing []:

const [isActive, setActive] = useState(false);

Depending on how your AppContext is set up, you may also need to use array destructuring for item and dispatch. Your next issue is that the above state only stores a boolean value. When it is true, it will be true for every value that you render while mapping. Meaning that when active is set to true all items will have the active class applied to them. To help manage this, you can change your state value to hold the id of the item that is active. Then in your mapping callback function, check if the current item id matches the id stored in your state, and if it does, use the active-color class, otherwise, use the card class.

const [activeItem, setActiveItem] = useState(-1);

const handler = (itemId) => {
 
  dispatch({ // you may want to conditionally call this,  but I'm basing it off your current logic
    type: 'SET_ITEM',
    payload: itemId,
  });
 
  setActiveItem(currentItem => currentItem === -1 ? itemId : -1); // toggle between -1 and itemPressed to make active state toggleable
};

return (
  <div className="container">
    <h2 className="smallheader">Please choose one</h2>
    <div className="flex flex-center">
      {ITEMS.map((item) =>
        <div className={activeItem === item.id ? 'active-color' : 'card'} key={item.id} onClick={() => handler(item.id)}>
        ...

  • Related