BottomNavigation does not update when navigating to new page

Viewed 530

I'm facing this issue when I'm building the bottom navigation bar for my website:

enter image description here

So it has 3 buttons, Home, Profile, Matches. I want to toggle between them. However, I noticed that, when I click on the Profile button, the page switches to the profile tab, but the button still stays in the same Home position until I double click it. So I debugged and found this issue:

enter image description here

Apparently when I click on the button, it stills stay on the same path? Here's my code for the bottom navigation:

import * as React from 'react';
import { Paper } from '@mui/material';
import BottomNavigation from '@mui/material/BottomNavigation';
import BottomNavigationAction from '@mui/material/BottomNavigationAction';
import ForumIcon from '@mui/icons-material/Forum';
import { Link } from 'react-router-dom';
import { withRouter } from 'react-router';

import HomeIcon from '@mui/icons-material/Home';
import PersonIcon from '@mui/icons-material/Person';

function BottomNav(props) {
  const [value, setValue] = React.useState(1);

  console.log('current path', props.location.pathname);

  const handleChange = () => {
    if(props.location.pathname == '/match'){
      setValue(0);
    }

    if(props.location.pathname == '/'){
      setValue(1);
    }

    if(props.location.pathname == '/profile'){
      setValue(2);
    }

    console.log('value is', value, 'on path', props.location.pathname);
  }

  return (
    <Paper sx={{ position: 'fixed', bottom: 0, left: 0, right: 0 }} elevation={3}>
      <BottomNavigation
        value={value}
        onChange={handleChange}
        showLabels
      >
        <BottomNavigationAction 
        component={Link}
        to="/match" 
        label="Matches" 
        icon={<ForumIcon />} />

        <BottomNavigationAction 
        component={Link}
        to="/" 
        label="Home" 
        icon={<HomeIcon />} />

        <BottomNavigationAction 
        component={Link}
        to="/profile" 
        label="Profile" 
        icon={<PersonIcon />} />
      </BottomNavigation>
    </Paper>
  );
}

export default withRouter(BottomNav);

This is the component that contains the navigation bar:

import React from "react";
import { Route, Switch } from "react-router-dom";
import Login from "../../Auth/Login";
import Logout from "../../Auth/Logout";
import Register from "../../Auth/Register";
import Dashboard from "../../Dashboard";
import Landing from "../../Landing";
import Profile from "../../Profile";
import Home from "../../Home"
import { Role } from "../Authentication";
import ProtectedRoute from "../Authentication/ProtectedRoute";
import ErrorRoute from "./ErrorRoute";
import CreateListing from '../../Listing/CreateListing';
import UpdateListing from '../../Listing/UpdateListing';
import ListingList from '../../Listing';
import ListingDisplay from '../../Listing/ListingDisplay';
import Forgot from "../../Auth/Forgot";
import AddBio from "../../Profile/AddBio";
import Match from "../../Match/Match";
import BottomNav from "../Navigation/BottomNav";

const Router = () => (
  <>
  <Switch>
    <Route exact path="/login" component={Login} />
    <Route exact path="/logout" component={Logout} />
    <Route exact path="/register" component={Register} />
    <Route exact path="/forgot" component={Forgot} />
    <Route exact path="/landing" component={Landing} />
    {/* Protected */}
    <ProtectedRoute exact roles={[Role.Admin]} path="/dashboard" component={Dashboard} />
    <ProtectedRoute exact roles={[Role.Flatee]} path="/addbio" component={AddBio} />
    <ProtectedRoute exact roles={[Role.Flat]} path="/newlisting" component={CreateListing} />
    <ProtectedRoute exact path="/" component={Home} />
    <ProtectedRoute exact roles={[Role.Flat]} path="/updatelisting" component={UpdateListing} />
    <ProtectedRoute exact roles={[Role.Flat]} path="/listings" component={ListingList} />
    <ProtectedRoute exact path="/listing/display" component={ListingDisplay} />
    <ProtectedRoute exact path="/profile" component={Profile} />
    <ProtectedRoute exact path="/match" component={Match} />
    <Route component={ErrorRoute} />
  </Switch>
  <BottomNav/>
  </>
);

export default Router;
2 Answers

That's because at the time when the handleChange callback fires, you're still on the current route, before react-router navigates to the new page, so the location.pathname is not set with the new route. And setState() sets the current index variable so nothing changes

const handleChange = () => {
  if(props.location.pathname == '/match'){
    setValue(0);
  }

  // when you're on home page and click the profile tab, setState(1) is called
  // which is meaningless
  if(props.location.pathname == '/'){
    setValue(1);
  }

  if(props.location.pathname == '/profile'){
    setValue(2);
  }

  console.log('value is', value, 'on path', props.location.pathname);
}

When react-router navigate to the new page, BottomNav will be re-rendered, you're supposed to get the index then based on the updated props.location.pathname:

function getPageIndex(route) {
  switch (route) {
    case '/match': return 0;
    case '/': return 1;
    case '/profile': return 2;
    default: return 0;
  }
}
function BottomNav(props) {
  const value = getPageIndex(props.location.pathname);

  return (
    <Paper sx={{ position: 'fixed', bottom: 0, left: 0, right: 0 }} elevation={3}>
      <BottomNavigation value={value} showLabels>
        <BottomNavigationAction 
          component={Link}
          to="/match" 
          label="Matches" 
          icon={<ForumIcon />} />
        {...}
      </BottomNavigation>
    </Paper>
  );
}

I had the same issue for someone else who maybe is reading, in my case I had the wrong import.. it should be:

import { Link } from "react-router-dom";
Related