Home > Net >  React collapsible menu component that should expand when I click on a category title but won't
React collapsible menu component that should expand when I click on a category title but won't

Time:12-29

I created a component for a collapsible navigation menu. This menu has a map of children categories and each one of these categories has a list of links.

This menu has a button to expand and collapse the navigation menu panel.

When I click in one of the category titles the menu should expand and at the same time show the corresponding links, however with this code only expands the navigation menu panel but not show the children links.

The collapsed structure is something like this:

Cat A
Cat B
Cat C

The expanded structure with children links is something like this:

Category A
|
|--Link A1
|--Link A2
|--Link A3

Category B
|
|--Link B1
|--Link B2
|--Link B3

Category C
|
|--Link C1
|--Link C2
|--Link C3

This is the menu component:

import React, { useState } from "react";
import { NavItem } from "./NavItem";

export function Navigation({
  menuItems,
}) {
  const [opened, setPanelOpened] = useState(false);

  return (
    <nav className={opened ? "expanded" : "collapsed"}>
      <button
        onClick={() => {
          setPanelOpened(!opened);
        }}
      >
        Toggle Menu
      </button>

      {menuItems.map((item) => (
        <NavItem menuItem={item} setPanelOpened={setPanelOpened} panelOpened={opened} />
      ))}
    </nav>
  );
}

This is the NavItem component for categories:

import React, { useState } from "react";

export function NavItem({
  menuItem,
  panelOpened,
  setPanelOpened,
}) {
  const [categoryOpened, setCategoryOpened] = useState(false);

  return (
    <div
      // If category is opened, should toggle a class to show / hide the children elements
      className={categoryOpened ? "opened" : "closed"}
    >
      <h2
        onClick={() => {
          // Should display the children menu items when I click the button
          setCategoryOpened(!categoryOpened);
          if (!panelOpened) {
            setPanelOpened(true);
          }
        }}
      >
        Parent Category
      </h2>

      {menuItem.children?.map((subItem) => (
        <a href={subItem.link}>
          {subItem.label}
        </a>
      ))}
    </div>
)
}

setCategoryOpened(!categoryOpened) won't work at the same time with setPanelOpened(true), only expands the menu and won't shows the children category items. How can I trigger both hooks at the same time?

When I click on a category title the menu should expand and show the corresponding children links at the same time, but only menu expansion happens, I have to click again to show the children links

CodePudding user response:

You're using two clicks events, if you want you can attach an useEffect hook that depends on your parent state, and when it changes the children set the states as true and open. As I cannot run your code, it's hard to debug, but the hint is to start putting console.log() to see where's the issue. Keep in mind that setState() method is asynchronous, so if you're trying to get the updated value in a sync function maybe it has the outdated value at that moment.

You can debug this as I told you with logs everywhere, to see what's happening. Hope it helps, if not please provide a code sandbox or a working snippet for us to debug properly your code.

I attached a example of the useEffect hook implemented on your component.

import React, { useState, useEffect } from "react";

export function NavItem({
  menuItem,
  panelOpened,
  setPanelOpened,
}) {
  const [categoryOpened, setCategoryOpened] = useState(false);
  
  useEffect(()=> {
    if(panelOpened) setCategoryOpened(true)
  }, [panelOpened])

  return (
    <div
      // If category is opened, should toggle a class to show / hide the children elements
      className={categoryOpened ? "opened" : "closed"}
    >
      <h2
        onClick={() => {
          // Should display the children menu items when I click the button
          setCategoryOpened(!categoryOpened);
          if (!panelOpened) {
            setPanelOpened(true);
          }
        }}
      >
        Parent Category
      </h2>

      {menuItem.children?.map((subItem) => (
        <a href={subItem.link}>
          {subItem.label}
        </a>
      ))}
    </div>
)
}

CodePudding user response:

use stopPropagation(e) in onclick. React can get confused when you import 2 states. We use it to avoid this.

ep :

onclick = {e => e.stopPropagation() ... }

  • Related