2

Using React Router and Material UI, I'm trying to set the navigation item selected state based on the current URL path. If a user comes to a specific page, it should set the selected state of the appropriate link in the sidebar.

This is the current setup, but not sure what property I'm missing to set the active state based on the URL path using React Router

data.navigation

"navigation":[
        {
            "label":"Introduction",
            "path": "/"  
        },
        {
            "label":"Page Two",
            "path": "pagetwo"
        }
]
import {BrowserRouter as Router, Route, Link, Switch} from 'react-router-dom';

const [selectedIndex, setSelectedIndex] = useState(0);

const handleListItemClick = (event, index) => {
   setSelectedIndex(index);
};

{data && data.navigation.map((item, i) =>
      <Link key={i} to={`${item.path}`} className={classes.link}> 
        <ListItem button selected={selectedIndex === i} onClick={(event) => handleListItemClick(event, i)}>
          <ListItemText className={classes.navLink}>{item.label}</ListItemText>
        </ListItem>
      </Link>
 )}
Matt
  • 1,561
  • 5
  • 26
  • 61
  • What's the value of `index` in `handleListItemClick` when you click? Code looks ok. – Sanish Joseph Sep 21 '21 at 16:57
  • Where do you want to use the active boolean? You could use the NavLink to get the active props. – jean182 Sep 21 '21 at 17:05
  • @jean182 The boolean could set a class that is currently used to style the selected nav link. – Matt Sep 21 '21 at 17:22
  • @SanishJoseph - the index is pulled from the `data.navigation.map` – Matt Sep 21 '21 at 17:23
  • If you just want to set active link some styles, why don't you try NavLink? it has a ActiveClass and active styles property which you can set. – Sanish Joseph Sep 21 '21 at 17:29
  • @SanishJoseph - I already have the active class styled, but it's a question of how to get that applied from the url when coming to the page externally. Not when the user has clicked a link. – Matt Sep 21 '21 at 17:31
  • For that you will need to identify the path from URL and set style according to that. If you use NavLink instead of Link it will be done automatically. https://reactrouter.com/web/api/NavLink – Sanish Joseph Sep 21 '21 at 17:33
  • @SanishJoseph - I updated to use `NavLink` and `exact`, but it doesn't seem to work either `````` – Matt Sep 21 '21 at 17:45
  • Did you apply `activeClassName` or `activeStyle`? Eg: `activeStyle={{ fontWeight: "bold", color: "red" }}` – Sanish Joseph Sep 21 '21 at 17:48
  • @SanishJoseph - That works, except the first link is still selected. The link associated with the view from the URL is styled, but how can I have it only style that and not the first link as well? – Matt Sep 21 '21 at 17:53
  • What's your 1st route? is it. Update the question with `data.navigation` values. Also try removing `exact`. Your 1st route seems to match for all other routes. – Sanish Joseph Sep 21 '21 at 17:57
  • @SanishJoseph - I've updated the question with my data source. Removed `exact`, but that doesn't help. – Matt Sep 21 '21 at 18:01
  • "path": "/pagetwo" Did you miss the / or it is set up that way? – Sanish Joseph Sep 21 '21 at 18:03
  • It works without the "/" – Matt Sep 21 '21 at 18:13
  • Check my answer. I have set up code for this question. – Sanish Joseph Sep 21 '21 at 18:15

2 Answers2

0

If you make each link a component, you can use useRouteMatch like in the "custom link" example in the React Router docs.

function NavigationLink(props) {
  const match = useRouteMatch({
    to: props.path,
  });

  // update the link class to set styles appropriately depending
  // on whether active is true or false
  const classes = useStyles({ active: match != null });

  return (
    <Link to={props.path} className={classes.link}>
      <ListItem button selected={props.selected} onClick={props.onClick}>
        <ListItemText className={classes.navLink}>{props.children}</ListItemText>
      </ListItem>
    </Link>
  );
}

// In your existing component
data.navigation.map((item, i) => (
  <NavigationLink
    key={i}
    path={item.path}
    selected={selectedIndex === i}
    onClick={(event) => handleListItemClick(event, i)}
  >
    {item.label}
  </NavigationLink>
));

Stephen Jennings
  • 12,494
  • 5
  • 47
  • 66
  • This doesn't seem to work, and I'm also getting the error that `match` is defined but not used. – Matt Sep 21 '21 at 17:25
  • Oh, haha, I forgot to actually use `match`. You'd probably want to pass that to useStyles and update the `link` class based on whether `match` is null or not. I've updated my answer. – Stephen Jennings Sep 21 '21 at 23:45
0

Here is a sample that I have set up to get this working. I had to keep exact to get it working.

https://stackblitz.com/edit/react-wvhugc?file=src/App.js

import React from 'react';
import './style.css';
import {
  BrowserRouter as Router,
  Switch,
  Route,
  NavLink,
} from 'react-router-dom';
import ListItemText from '@mui/material/ListItemText';
import ListItem from '@mui/material/ListItem';

export default function App() {
  const navigation = [
    {
      label: 'Introduction',
      path: '/',
    },
    {
      label: 'Page Two',
      path: '/pagetwo',
    },
  ];
  return (
    <Router>
      {navigation.map((item, i) => (
        <NavLink
          exact
          activeStyle={{
            fontWeight: 'bold',
            color: 'red',
          }}
          key={i}
          to={`${item.path}`}
        >
          <ListItem button>
            <ListItemText>{item.label}</ListItemText>
          </ListItem>
        </NavLink>
      ))}
      <Switch>
        <Route exact path="/">
          <div>Home</div>
        </Route>
        <Route path="/">
          <div>Page 2</div>
        </Route>
      </Switch>
    </Router>
  );
}
Sanish Joseph
  • 2,140
  • 3
  • 15
  • 28
  • Can the `activeStyle` be set to a class? I'm losing previous functionality of setting the style of the currently selected link, but this is setting the font color. How can I keep this new functionality, but also have it style the link for focused, active, and selected states? – Matt Sep 21 '21 at 18:26
  • Yes. `activeClassName="selected"` Give your styles in that class. You can use it instead of `activeStyle ` – Sanish Joseph Sep 21 '21 at 18:57
  • Just needed to set the styles for the class of the selected link. Thanks! – Matt Sep 21 '21 at 20:21