Skip to content

How to manipulate a div using Active States in React?

I am trying to make a menu with three accordion windows. All of the data is being stored within the functional components using arrays, my aim is to trigger the opening of individual windows separately using the Active States.

This is my code so far:

const Accordion = () => {

 const accordionItems = [
    { id: 'menu1', text: 'text from the first menu' },
    { id: 'menu2', text: 'text from the second menu' },
    { id: 'menu3', text: 'text from the third menu' },
  ];

    const [activeItem, setActiveItem] = useState();
    const [setActive, setActiveState] = useState("");
    const [setHeight, setHeightState] = useState("0px");

    const content = useRef(null);

    function toggleAccordion() { 

     setActiveState(setActive === "" ? "active" : "");

     setHeightState(
       setActive === "active" ? "0px" : `100px`
    );  
   }

   return (
      <div class ="my_list"> 
         {accordionItems.map((p, index) => {
            return (
               <div className="accordion__section">
                <button
                  className={`accordion ` + (activeItem === p.id ? 'active' : '')}
                  onClick={() => {
                  setActiveItem(p.id);
                  toggleAccordion();
                }}>
                <p className="accordion__title">{p.id}</p>  
                </button>   
       
              <div ref={content}  style={{ maxHeight: `${setHeight}` }} className= 
              {"accordion__content "  + (activeItem === p.id ? 'active' : '')}> 

              <div className="accordion__text"> {p.text} </div>

             </div>
            </div>
          );
        })}
     </div>
    )
};

As you can see with the code above I have a simple activeItem state, the functionality of which is embedded into the button class as well as the content. This is working fine in terms of isolating divs adding simple styling to the active class in my css file.

My issue is finding a way to trigger the ToggleAccordion() function on a single div, the one that is active, as it stands all of my accordion windows are opening and closing regardless as to the one I click.

I have attempted to compound both the setActive and activeItem variables into the same variable, but I can’t think of a solution that doesn’t lose the functionality of either the windows opening or the css styling that I mentioned earlier. My hunch is that the internal setActive and setActiveSate within the toggle function need to reference the activeItem.

any assistance or suggestions would be appreciated tonnes.

Thanks p.s also knew to react

Answer

The way you’ve built this currently, you’re exactly right about needing to reference the active item.

I would completely remove [setActive, setActiveState] and [setHeight, setHeightState] as you can control everything just by changing the activeItem.

Based on the id of the activeItem in your map, you can tell the accordion to be open or not. I would try something like this:

<div 
  ref={content}
  style={{ maxHeight: `${activeItem === p.id ? "100px" : "0px" }` }}
  className={"accordion__content "  + (activeItem === p.id ? 'active' : '')}
>

And then to handle your accordion being deselected, update your onClick from:

onClick={() => {
  setActiveItem(p.id);
  toggleAccordion();
}}

to this:

onClick={() => {
  setActiveItem(activeItem === p.id ? '' : p.id);
}}

You can completely get rid of your toggleAccordion() function. In the updated onClick above, you are checking if the current id is the same as the one you are clicking, and removing it if it is (deselecting all accordions), if it’s not the active accordion then it becomes the new active accordion.