Keeping Open Sidemenu

46 Views Asked by At

I want to keep open my sidemenu but i don't know how to do.

    const Dep2Handler = (e) => {
      const classOnTarget = e.target.parentElement;
      const onRemoveTarget = classOnTarget.parentElement.children;

      if (classOnTarget.classList.contains("on")) {
          classOnTarget.classList.remove("on");
      }
      else {
        for (let i = 0; i < onRemoveTarget.length; i++) {
          onRemoveTarget[i].classList.remove("on");
        }
        classOnTarget.classList.add("on");
      }      
     
    };
/*     useLayoutEffect(()=>{
      
    },[sideNaviPos]) */

    const Dep3Handler = (e,props) => {   
      const classOnTarget = e.target;
      const onRemoveTarget = classOnTarget.parentElement.children;

      classOnTarget.classList.add("on");
    };


--------------------------------------------------------------------
 <ol>
          <li>
            <p onClick={Dep2Handler} sidebar={sidebar}></p>
            <ol className="has_dep3">
              <li onClick={Dep3Handler} value="space"><button className="btn" onClick={(e)=>history("/info")}></button></li>
              <li onClick={Dep3Handler} value="equip"><button className="btn" onClick={(e)=>history("/info/info/equipinfo")}></button></li>
              <li onClick={Dep3Handler} value="work"><button className="btn" onClick={(e)=>history("/info/info/workerinfo")}></button></li>
            </ol>
          </li>
          <li>
            <p onClick={Dep2Handler}></p>
            <ol className="has_dep3">
              <li onClick={Dep3Handler}><button onClick={(e)=>history("/admin/info/greetings")}></button></li>
              <li onClick={Dep3Handler}><button onClick={(e)=>history("/admin/info/vision")}></button></li>
              <li onClick={Dep3Handler}><button onClick={(e)=>history("/admin/info/organization")}></button></li>
              <li onClick={Dep3Handler}><button onClick={(e)=>history("/admin/info/partner")}></button></li>
            </ol>
          </li>
          <li>
         <p onClick={(e)=>{Dep2Handler(e);history("/info/way")}}></p>
          </li>
          <li>
          <p onClick={(e)=>{Dep2Handler(e);history("/info/faq")}}></p>
          </li>
        </ol>

if i click children of first li menu, keeping open then click another one the previous will be close and clicked menu open. Is it possible without use redux?

1

There are 1 best solutions below

0
nbermudezs On

There is quite a bit to unpack here so I apologize in advance if this post ends up longer than you desired.

First off, and to address your question about Redux

Is it possible without use redux?

Most definitely. Lots of people used React before redux was a thing and for some apps even today can get away without redux (using the Context API is an option).

Before just showing you to code let me comment on some of your existing code

classOnTarget.classList.add("on");

by doing this you are effectively using the DOM's classList to hold your state rather than React. While there are cases that something like that might be needed (e.g. you are introducing some react components into an existing codebase) I don't think that's the case here and we should change that.

Let's start small, what if you want to have a single menu open or close. One approach could be to have something like

const [isOpen, setIsOpen] = useState(false);
...
return <div className={isOpen ? 'on' : ''}>...</div>

The, maybe obvious, next step is what if I have two menus?

const [isFirstOpen, setIsFirstOpen] = useState(false);
const [isSecondOpen, setIsSecondOpen] = useState(false);
...
return (
  <div className={isFirstOpen || isSecondOpen ? 'on' : ''}>
    <div className={isFirstOpen ? 'active' : ''}>First</div>
    <div className={isSecondOpen ? 'active' : ''}>Second</div>
  </div>
);

but the above code is neither pretty nor scalable (what if I have 10 items in the menu?). So instead of representing the state of each possible menu you might want to keep track of which one is open/active

const [activeRoute, setActiveRoute] = useState();
...
return (
  <div className={activeRoute ? 'on' : ''}>
    <div className={activeRoute === 'first' ? 'active' : ''}>First</div>
    <div className={activeRoute === 'second' ? 'active' : ''}>Second</div>
  </div>
);

Now, we want the active route to change based on what is being clicked

const [activeRoute, setActiveRoute] = useState();
const navigateTo = (newRoute) => {
  history(newRoute);
  setActiveRoute(newRoute);
};
...
return (
  <div className={activeRoute ? 'on' : ''}>
    <div className={activeRoute === '/first' ? 'active' : ''}
      onClick={() => navigateTo('/first')}>First</div>
    <div className={activeRoute === '/second' ? 'active' : ''}
      onClick={() => navigateTo('/second')}>Second</div>
  </div>
);

Now you might ask, well that's all nice and dandy but how do I deal with a nested menu? like in your case.

You might notice in your existing code that all the onClick handlers happen in the deepest (the most nested) li / button, not on the first level of the menu.

So lets say that on your first level you just want to add a class so it looks like that's the active menu.

const MENU_MAP = {
  '/first': 'menuA',
  '/second': 'menuA',
  '/third': 'menuB',
  '/fourth': 'menuB',
};

const [activeRoute, setActiveRoute] = useState();
const navigateTo = (newRoute) => {
  history(newRoute);
  setActiveRoute(newRoute);
};
const activeMenu = activeRoute ? MENU_MAP[activeRoute] : null;
...
return (
  <div>
    <div className={activeMenu === 'menuA' ? 'open' : ''}>
      <div className={activeRoute ? 'on' : ''}>
        <div className={activeRoute === '/first' ? 'active' : ''}
          onClick={() => navigateTo('/first')}>First</div>
        <div className={activeRoute === '/second' ? 'active' : ''}
          onClick={() => navigateTo('/second')}>Second</div>
      </div>
    </div>
    <div className={activeMenu === 'menuB' ? 'open' : ''}>
      <div className={activeRoute ? 'on' : ''}>
        <div className={activeRoute === '/third' ? 'active' : ''}
          onClick={() => navigateTo('/third')}>Third</div>
        <div className={activeRoute === '/fourth' ? 'active' : ''}
          onClick={() => navigateTo('/fourth')}>Fourth</div>
      </div>
    </div>
  </div>
);

I hope you can see a pattern in the above and apply that to your specific HTML requirements of using ol, li, button. Hope this helps

--

For anyone other than the OP reading this and thinking "this could be cleaner" or "we could use useContext" or "we could add an abstraction here and here", save it. The verbosity on my answer to hopefully help the OP see the pattern so they can figure out a solution; this is not about showing off you know redux or mobX or how to use hooks.