Home > other >  How to check if parent element include children with spesific class with useEffect and UseRef?
How to check if parent element include children with spesific class with useEffect and UseRef?

Time:06-17

I want to accordion-content class if include link-active or not. So I created the code like that.

import {React,useRef,useEffect,useState} from 'react';

export function App(props) {
    const accordionOperation = useRef(null);
    const [isOperationActive, setOperationActive] = useState(false);
    const [isOperationOpen, setOperationOpen] = useState(false);


        useEffect(() => {
        const accordionOperationChildren = accordionOperation.current?.children;
        for (let i = 0; i < accordionOperationChildren?.length; i  ) {
            if (accordionOperationChildren[i].classList.contains("link-active")) {
                setOperationActive(true);
            } else {
                setOperationActive(false);
            }
        }

    }, []);


  return (
    <div className='App'>
      <ul className="accordion-content" ref={accordionOperation}>
                                        <NavLink to="/hc20/bee" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Bees</li>
                                        </NavLink>
                                        <NavLink to="/hc20/command" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Commands</li>
                                        </NavLink>
                    </ul>
    </div>
  );
}



But my hook called isOperationActive doesnt change according to active-link I am using Navlink components from react-dom. So when I click on any link the link become active.

enter image description here

So how can I keep my isOperationActive hook updated?

This is rest of my code



    <div className="accordion operations">
                            <div className="accordion-item">
                                <div
                                    className="accordion-title"
                                    role="button"
                                    tabIndex={0}
                                    onClick={() => setOperationOpen(!isOperationOpen)}
                                >
                                    <div className={isOperationOpen ? "accordion-title-head " : "accordion-title-head"}>
                                        <ArrowsHorizontal style={{ paddding: "4px" }} />
                                        <div className={"dropdownOperation"}>Operations</div>
                                        <div className="text-white">{isOperationOpen ? <ChevronUp /> : <ChevronDown />}</div>
                                    </div>
                                </div>
                                {isOperationOpen && (
                                    <ul className="accordion-content" ref={accordionOperation}>
                                        <NavLink to="/hc20/bee" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Bees</li>
                                        </NavLink>
                                        <NavLink to="/hc20/command" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Commands</li>
                                        </NavLink>
                                        <NavLink to="/hc20/company" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Companies</li>
                                        </NavLink>
                                        <NavLink to="/hc20/dashboard" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Dashboards</li>
                                        </NavLink>{" "}
                                        <NavLink
                                            to="/hc20/device-profile"
                                            className={({ isActive }) => (isActive ? "link-active" : "disable")}
                                        >
                                            <li className="aside_list-item">Device Profiles</li>
                                        </NavLink>
                                        <NavLink to="/hc20/location" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Location</li>
                                        </NavLink>{" "}
                                        <NavLink to="/hc20/route" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Routes</li>
                                        </NavLink>{" "}
                                        <NavLink to="/hc20/sub" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Subscriptions</li>
                                        </NavLink>
                                        <NavLink to="/hc20/trackable" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Trackables</li>
                                        </NavLink>{" "}
                                        <NavLink to="/hc20/user" className={({ isActive }) => (isActive ? "link-active" : "disable")}>
                                            <li className="aside_list-item">Users</li>
                                        </NavLink>
                                    </ul>
                                )}
                            </div>
                        </div>

CodePudding user response:

At the moment, you're assuming that once your if-statement within your for loop evaluates to true and performs setOperationActive(true); your for loop will stop checking for other elements. But this doesn't happen. The subsequent iterations of your for loop that look at your other elements will call setOperationActive(false); in the else-block, as a result not setting your state to true as you expect.

Generally, if you find yourself trying to set state in a loop (that doesn't break when you do so), you're most likely doing something wrong. The idea is that you should only need to set your state value once. What you set that state value to though can be computed with a loop, but the setting of that value should only need to occur once, eg:

const accordionOperationChildren = accordionOperation.current?.children;
let active = false; // determines what we set out state value to
for (let i = 0; i < accordionOperationChildren?.length && !active; i  ) { // stop looping if active is true or we've reached the end of our children
  if (accordionOperationChildren[i].classList.contains("link-active")) {
    active = true;
  } 
}
setOperationActive(active); // only call this once

There are other ways we can write the above, such as using .some():

const accordionOperationChildren = Array.from(accordionOperation.current?.children ?? []);
const active = accordionOperationChildren.some(
  elem => elem.classList.contains("link-active")
);
setOperationActive(active);

Or you can use .querySelector() to check if you have a match:

const activeLink = Boolean(accordionOperation.current?.querySelector(".active-link"));
setOperationActive(activeLink);

the above is slightly different from what you're doing, as this doesn't just search the immediate children of accordion-content, but it also searches the nested children (ie: each <a> tag's children) for an element that matches the active-link class. If this is an issue (which juding by your HTML screenshot doesn't look like it will be), you could also use :scope pseudo-class (bearing in mind its browser availability) with the child combinator selector:

const activeLink = Boolean(accordionOperation.current?.querySelector(":scope > .active-link"));
setOperationActive(activeLink);
  • Related