Skip to content
Advertisement

React how to make conditionally rendered navigation persist

I am in the process of learning React and am creating a React desktop app where the side-navigation must change depending on the user’s current location (determined by standard navgation buttons in the App body).

The application is accessible here: https://codesandbox.io/s/conditional-navigation-di8t5?file=/pages/Start.jsx

I am able to make the navigation options render conditionally as the user moves through the app. See for example: from ‘Start’ to ‘Page A’, to ‘Page B’. The problem is when they move back to a previous location, the navigation also reverts. I want it to be so that once the user has reached Page B and goes back to Page A, Page B is still accessible in the side nav.

From research it seems that using the React Context API would be the best solution. Is this the case, or is there an easier method that I am missing? Thank you.

Advertisement

Answer

You can can take the approach mentioned by c0m1t of using useLocation to track the current location.

To be able to track the location consistently and for other more organisational reasons I would also advise you to change your application structure somewhat.

I think one problem with the current structure is that Layout is a child of each of your page components. Instead I think this component fits more as a parent of your pages, instead of having every page render a Layout component. The same applies to your Header component.

I would recommend to change your App.js file to something like this:

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Header />
      <Layout>
        <Switch>
          <Route exact path="/" component={Start} />
          <Route exact path="/page-A" component={PageA} />
          <Route exact path="/page-B" component={PageB} />
          <Route exact path="/page-C" component={PageC} />
        </Switch>
      </Layout>
    </ThemeProvider>
  );
}

I also don’t think you need a different Menu component for each screen.

You could instead have a general Menu component and render this component inside your Layout component:

const Layout = (props) => {
  return (
    <React.Fragment>
      <Grid container>
        <Grid item xs={3} className="side-nav">
          <Menu />
        </Grid>
        <Grid item xs={9} className="main">
          <Box p={4}>{props.children}</Box>
        </Grid>
      </Grid>
    </React.Fragment>
  );
};

Then you could change the Menu component to this:

const pageLocations = ["/page-A", "/page-B", "/page-C"];
const pages = ["Page A", "Page B", "Page C"];

const Menu = () => {
  const location = useLocation();
  const [locations, setLocations] = React.useState([]);

  React.useEffect(() => {
    const currentPath = location.pathname;
    if (!locations.includes(currentPath)) {
      setLocations([...locations, currentPath]);
    }
  }, [location, locations, setLocations]);

  return (
    <Box>
      <MenuList>
        {pages.map((page, i) => {
          return (
            <MenuItem
              key={i}
              to={pageLocations[i]}
              className={locations.includes(pageLocations[i]) ? "" : "deactive"}
              component={NavLink}
            >
              {page}
            </MenuItem>
          );
        })}
      </MenuList>
    </Box>
  );
};

So the approach is to keep track of all pages visited before from the menu. A location is mapped to each menu item in a way where we can check whether a menu item should be shown based on all locations already visited and the location mapped to the menu item.

This answer doesn’t handle the case where you start from the url of a particular page like /page-A instead of /.


sandbox example

Advertisement