abcbc
abcbc

Reputation: 121

How to map routes with individual ListItem in reactjs?

Im trying to use material Ui with my react frontend project. I have implemented the AppBar and drawer component and they are working just fine. This is what I have achieved till now :

export default function MiniDrawer() {
  const classes = useStyles();
  const theme = useTheme();
  const [open, setOpen] = React.useState(false);

  const handleDrawerOpen = () => {
    setOpen(true);
  };

  const handleDrawerClose = () => {
    setOpen(false);
  };

  return (
    <div className={classes.root}>
      <CssBaseline />
      <AppBar style={{background: 'black'}}
        position="fixed"
        className={clsx(classes.appBar, {
          [classes.appBarShift]: open,
        })}
      >
        <Toolbar>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            onClick={handleDrawerOpen}
            edge="start"
            className={clsx(classes.menuButton, {
              [classes.hide]: open,
            })}
          >
            <MenuIcon />
          </IconButton>
          <Typography className={classes.mystyle} variant="h5" >
            CodeBasics
          </Typography>
        </Toolbar>
      </AppBar>
      <Drawer
        variant="permanent"
        className={clsx(classes.drawer, {
          [classes.drawerOpen]: open,
          [classes.drawerClose]: !open,
        })}
        classes={{
          paper: clsx({
            [classes.drawerOpen]: open,
            [classes.drawerClose]: !open,
          }),
        }}
        open={open}
      >
        <div className={classes.toolbar}>
          <IconButton onClick={handleDrawerClose}>
            {theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
          </IconButton>
        </div>
        <Divider />
        <List>
          {['Home', 'Algorithms', 'DataStructures'].map((text, index) => (
            <ListItem button key={text}>
              <ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
              <ListItemText primary={text} />
            </ListItem>
          ))}
        </List>
        <Divider />
      </Drawer>
      <main className={classes.content}>
        <div className={classes.toolbar} />
        <Layout>
              <Router>
                <Switch>
                  <Route exact path="/" component={Home} />
                  <Route path="/about" component={About} />
                  <Route path="/DataStructures" component={DataStructures} />
                </Switch>
              </Router>
            </Layout>
      </main>
    </div>
  );
}

Now I want to map each 'Home', 'DataStructures' and 'Algorithms' with a different route. I have achieved a way to a route the List component in this way.

         <List>
          {['Home', 'Algorithms', 'DataStructures'].map((text, index) => (
            <ListItem button key={text} component="a" href="www.google.com">
              <ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
              <ListItemText primary={text} />
            </ListItem>
          ))}
        </List>

but with its only getting me go to the same link for every item in the list. How do I map individual list items with individual routes?

Upvotes: 2

Views: 2965

Answers (2)

Duc Hong
Duc Hong

Reputation: 1189

I've put together a small sample. This structure works for me for all of my projects and quite fast to implement once you've got the hang of it.

Demo link

Route Structure

This pathIds variable is an object which holds all id values for routes. This will be useful when needing a reference to auto generate breadcrumb links.

const pathIds = {
  home: 'home',
  ...
}

This pathRouting variable is an object which holds all path routing. Just path routing.

const pathRouting = {
  home: '/home',
  ...
}

Primary structure object:

const pageRoutes = {
  [pathIds.home]: {
    path: pathRouting.home,
    sidebarName: 'Homepage',
    icon: Dashboard,
    noRender: false,
    component: Home,
  },
  ...
}

This is primary structure object, holding all necessary info to generate onto HTML (sidebar, breadcrumbs, routes etc).

  • path: get the path value from pathRouting object. (Required)
  • sideName: text showing on Sidebar. (Required)
  • icon: usually this icon only shows on the sidebar. You can omit this if your menu/sidebar/navigation doesn't need it. (Optional)
  • noRender: boolean to indicate whether this object will be rendered to the sidebar. This is useful for routes that doesn't need to directly access (like a 404 Page Not Found page) (Optional)
  • component: The React component which will be used to render. (Required)

Then, in your app.js, you import pageRoutes as usual. Before doing mapping or iteration work, you might need to convert this object to an array (or use a library like lodash to directly iterating on the object).

const routeArray = Object.values(pageRoutes);

Then:

<Switch>
  {routeArray.map((prop, key) => {
    return (
      <Route
        path={prop.path}
        component={prop.component}
        exact={prop.exact || false}
        key={`route-${key}`}
      />
    );
  })}
  <Route component={pageRoutes[pathIds.error404].component} />
</Switch>

Notice the very last line where I explicitly declare a fallback <Route /> path in case user is on a wrong/ not defined path, that's how useful an object is in this kind of situation. It doesn't need path prop.

You sidebar is just a simple component receives a list of routes as props (as converted in app.js), then it can be used to show on the view.

<List component="nav" aria-label="main mailbox folders">
  {routes.map(({ path, noRender, sidebarName, ...prop }, index) => {
    if (noRender) return null;

    return (
      <NavLink to={path} key={`route-${index}}`}>
        <ListItem button>
          <ListItemIcon>
            <prop.icon />
          </ListItemIcon>
          <ListItemText primary={sidebarName} />
        </ListItem>
      </NavLink>
    );
  })}
</List>

You will see it only renders Homepage, Inbox and Reset Password on sidebar. But you can directly enter /register to make <Register /> show up. The same for /page-not-found. Or even when you enter a wrong address like /register-user, the <PageNotFound /> component will be used for fallback case.

You might wonder why I don't put all path routes and path Ids in its own place, why do I need to move them into a separate object? The answer is, in my case I usually need quick access to those values. This might be useful for generating breadcrumbs or do some iteration without pulling the whole object. Depend on your specific project needs, those two might be shortened.

For full code, you can visit here: Sample Link

Upvotes: 1

abcbc
abcbc

Reputation: 121

I have found a way to achieve this. First I created an array of objects like:

 const topics= [{
      topic: "Home",
      path: "home"
  },
{
    topic: "DataStructures",
    path: "datastructures"
}]

then I did this:

<List>
          {topics.map((text, index) => (
            <ListItem button key={text.topic} component="a" href={text.path === "home"? "\\":text.path}>
              <ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
              <ListItemText primary={text.topic} />
            </ListItem>
          ))}
        </List>

Upvotes: 0

Related Questions