question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Doc] Multiple states within a map

See original GitHub issue

Nice library! I am currently struggling to find the most optimal way to use the hooks within a map to keep track of multiple menus’ state.

This is the dirty code - really not optimal - I came up with so far:

export default () => {
  const resourcesMenuState = usePopupState({
    popupId: "resources",
    variant: "popover",
  });
  const servicesMenuState = usePopupState({
    popupId: "services",
    variant: "popover",
  });

  return (
    <nav>
      <MenuList disablePadding className={classes.menuList}>
        {routes.map((route) => (
          <div key={uid(route)}>
            <MenuItem
              {...(route.name === "Resources" && bindHover(resourcesMenuState))}
              {...(route.name === "Services" && bindHover(servicesMenuState))}
              // --- SNIP ---
            >
              {route.name}
            </MenuItem>
            {route.routes && (
              <Menu
                {...(route.name === "Resources" &&
                  bindMenu(resourcesMenuState))}
                {...(route.name === "Services" && bindMenu(servicesMenuState))}
                // --- SNIP ---
              >
                {route.routes.map((nestedRoute) => (
                  <MenuItem
                    key={uid(nestedRoute)}
                    // Dirty hack - can handle only 2 menus
                    onClick={
                      route.name === 'Resources'
                        ? resourcesState.close
                        : servicesState.close
                    }
                    // --- SNIP ---
                  >
                    {nestedRoute.name}
                  </MenuItem>
                ))}
              </Menu>
            )}
          </div>
        ))}
      </MenuList>
    </nav>
  );
};

Could you please provide an optimal example on how to achieve this?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
jedwards1211commented, Sep 10, 2021

@aress31 just got back from a vacation. Sorry for forgetting about this ticket. Here’s how I would recommend solving it:

import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import MenuList from "@material-ui/core/MenuList";
import {
  bindMenu,
  bindTrigger,
  usePopupState,
} from "material-ui-popup-state/hooks";
import React from "react";
import { Link as RouterLink, useLocation } from "react-router-dom";
import { uid } from "react-uid";
import routes from "../../constants/routes";


function RouteMenuItem({ route }) {
  const popupState = usePopupState({
    popupId: route.name,
    variant: "popover",
  });
  const {isOpen, close} = popupState;
  return (
    <React.Fragment>
      <MenuItem
        {...route.routes && bindTrigger(popupState)}
        selected={route.path === location.pathname}
        to={!route.routes && route.path}
      >
        {route.name}
      </MenuItem>
      {route.routes && (
        <Menu
          {...bindMenu(popupState)}
          onClick={close}
          keepMounted
          anchorOrigin={{
            horizontal: "center",
            vertical: "bottom",
          }}
          getContentAnchorEl={null}
          transformOrigin={{
            horizontal: "center",
            vertical: "top",
          }}
        >
          {route.routes.map((nestedRoute) => (
            <MenuItem
              key={uid(nestedRoute)}
              component={RouterLink}
              selected={nestedRoute.path === location.pathname}
              to={nestedRoute.path}
              onClick={popupState.close}
            >
              {nestedRoute.name}
            </MenuItem>
          ))}
        </Menu>
      )}
    </React.Fragment>
  );
}

export default () => {
  return (
    <nav>
      <MenuList disablePadding className={classes.menuList}>
        {routes.map((route) => (
          <RouteMenuItem key={uid(route)} route={route} />
        )}
      </MenuList>
    </nav>
  );
};

Here are the reasons I’m not in favor of a setter for popupId or being able to use a single usePopupState for multiple popup menus:

  • It’s not really compatible with the existing API. I can’t see a way of keeping the existing API and also getting a setter for popupId to work
  • Why re-render the entire top-level component when just one submenu is opened or closed? With the example I gave above, only the submenu that was opened or closed rerenders.
  • It would be impossible to memoize event handler callback functions. The current implementation of bindTrigger(popupState) returns an object with onClick: popupState.open, where popupState.open is a memoized function. If I made a bindTrigger(popupState, { popupId: 'foo' }) API, then it would have to return something like onClick: () => popupState.open({ popupId: 'foo' }), causing the MenuItem to rerender each time because onClick is always a new function instance.
0reactions
aress31commented, Sep 10, 2021

@jedwards1211 too advanced for me, I am just a hobbyist thanks for the warning though but yeah as you said number of routes is and will remain constant so in my case I guess it is ok…

Read more comments on GitHub >

github_iconTop Results From Across the Web

Map - AWS Step Functions
Map states can use a variety of input types, including a JSON array, a list of Amazon S3 objects, or a CSV file....
Read more >
Display Multiple State Values in Map Visualization
Display Multiple State Values in Map Visualization ; 1, Alabama; Alaska ; 2, Alabama ; 3, Alabama; Arkansas ; 4, California; New York;...
Read more >
How to Update State in map function in react - Stack Overflow
Updating the state inside the map will cause multiple renderings(as the state is updating) and it's not good.
Read more >
Adding a Map and Markers to a React Application
Adding a Map and Markers to a React Application · Overview · Install @googlemaps/react-wrapper · Add a map component · Extend map component...
Read more >
DAIFacilitiesMap.pdf - WI DOC
Fond du Lac. Rock. Winnebago. Iron. Rusk. Oneida. Door. Sawyer. Grant. Wood. Brown. Clark. Oconto. Green. Racine. Pepin. Waupaca. Buffalo. Iowa. Dane.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found