Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Material UI BottomNavigation component Routing Issue

I'm trying to implement the BottomNavigation Component from Material UI and i have an issue with when the user uses the back and forward buttons of the browser.

The problem is, that the BottomNavigation Component is configured to change page in the the layout when a navigation item button is pressed. What it doesn't do however, is change the selected index of the BottomNavigation items when the browser is used to go back to the previous page.

What im left with is an inconsistent state.

How do i go about changing the selected index of the navigation component when the browser buttons are used?

Here is how the UI looks :-

[Material UI Layout[1]

Here is the Root Component :-

import React from 'react'
import {browserHistory, withRouter} from 'react-router';
import {PropTypes} from 'prop-types'
import {connect} from 'react-redux'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
import AppBar from 'material-ui/AppBar'
import Paper from 'material-ui/Paper'
import MyBottomNavigation from '../material-ui/MyBottomNavigation'

const style = {
    padding: 10,
    height: '85vh'

  }

class Root extends React.Component {
    constructor(props) {
        super(props)   

        this.state  = {
            bottomNavItemIndex : 0
        }
    }


    render() {
        return (
            <MuiThemeProvider>
                <div>
                    <AppBar title="Pluralsight Admin" 
                        iconClassNameRight="muidocs-icon-navigation-expand-more"
                        showMenuIconButton={false}
                        zDepth={1}
                        />
                    <Paper zDepth={1} style={style}>
                        {this.props.children}
                    </Paper>
                    <MyBottomNavigation/>
                </div>
            </MuiThemeProvider>

        )
    }
}


Root.propTypes = {
    children: PropTypes.object.isRequired
}



export default Root

And here is the Navigation Component :-

class MyBottomNavigation extends Component {
    constructor(props){
        super(props)
        this.state = {
            selectedIndex: 0
          }

        this.selectBottomNavigationItem = this.selectBottomNavigationItem.bind(this)
    }

  selectBottomNavigationItem(index){
    this.setState({selectedIndex: index})

    switch(index) {
        case 0:
        return browserHistory.push('/')
        case 1:
        return browserHistory.push('/courses')
        case 2:
        return browserHistory.push('/authors')
        default:
        return browserHistory.push('/')
    }
  }


  render() {
    return (
      <Paper zDepth={1}>
        <BottomNavigation selectedIndex={this.state.selectedIndex}>

          <BottomNavigationItem
            label="Home"
            icon={recentsIcon}
            onClick={() => this.selectBottomNavigationItem(0)}
          />

          <BottomNavigationItem
            label="Course"
            icon={favoritesIcon}
            onClick={() => this.selectBottomNavigationItem(1)}
          />

          <BottomNavigationItem
            label="Authors"
            icon={nearbyIcon}
            onClick={() => this.selectBottomNavigationItem(2)}
          />
        </BottomNavigation>
      </Paper>
    )
  }
}


export default MyBottomNavigation
like image 638
Derek Avatar asked Jan 25 '18 13:01

Derek


2 Answers

Just got an implementation working!

The trick is to make a new navbar component that wraps the Material UI BottomNavigation and exports it with the react-router-dom's withRouter higher order function. Then you can do some fiddling with the current route passed into the props and set the value of the BottomNavigation component based on an array of routes (which route corresponds to which value).

My code works a bit differently than what you posted originally, I'm just going off of the BottomNavigation example here and the example of usage with react-router-dom here.

Here is my implementation:

/src/App.js
import React, {Component} from 'react';
import './App.css';
import {BrowserRouter as Router, Route} from 'react-router-dom';
import PrimaryNav from './components/PrimaryNav';

// Views
import HomeView from './views/HomeView';

class App extends Component {
  render() {
    return (
      <Router>
        <div className="app">
          <Route path="/" component={HomeView} />        
          <PrimaryNav />
        </div>
      </Router>
    );
  }
}

export default App;
/src/components/PrimaryNav.js
import React, {Component} from 'react';
import {Link, withRouter} from 'react-router-dom';
import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';
import LanguageIcon from '@material-ui/icons/Language';
import GroupIcon from '@material-ui/icons/Group';
import ShoppingBasketIcon from '@material-ui/icons/ShoppingBasket';
import HelpIcon from '@material-ui/icons/Help';
import EmailIcon from '@material-ui/icons/Email';
import './PrimaryNav.css';

class PrimaryNav extends Component {
  state = {
    value: 0,
    pathMap: [
      '/panoramas',
      '/members',
      '/shop',
      '/about',
      '/subscribe'
    ]
  };

  componentWillReceiveProps(newProps) {
    const {pathname} = newProps.location;
    const {pathMap} = this.state;

    const value = pathMap.indexOf(pathname);

    if (value > -1) {
      this.setState({
        value
      });
    }
  }

  handleChange = (event, value) => {
    this.setState({ value });
  };

  render() {
    const {value, pathMap} = this.state;

    return (
      <BottomNavigation
        value={value}
        onChange={this.handleChange}
        showLabels
        className="nav primary"
      >
        <BottomNavigationAction label="Panoramas" icon={<LanguageIcon />} component={Link} to={pathMap[0]} />
        <BottomNavigationAction label="Members" icon={<GroupIcon />} component={Link} to={pathMap[1]} />
        <BottomNavigationAction label="Shop" icon={<ShoppingBasketIcon />} component={Link} to={pathMap[2]} />
        <BottomNavigationAction label="About" icon={<HelpIcon />} component={Link} to={pathMap[3]} />
        <BottomNavigationAction label="Subscribe" icon={<EmailIcon />} component={Link} to={pathMap[4]} />
      </BottomNavigation>
    );
  }
}

export default withRouter(PrimaryNav);

And here's my version numbers for good measure:

"@material-ui/core": "^1.3.1",
"@material-ui/icons": "^1.1.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
like image 130
saricden Avatar answered Oct 23 '22 08:10

saricden


Just found a really neat solution for this here:

Essentially you just create a pathname constant each render, using window.location.pathname and make sure that the value prop of each <BottomNavigationAction /> is set to the same as the route (including preceding forward slash) ...something like:

const pathname = window.location.pathname
const [value, setValue] = useState(pathname)
const onChange = (event, newValue) => {
    setValue(newValue);
}
return (
    <BottomNavigation className={classes.navbar} value={value} onChange={onChange}>
        <BottomNavigationAction component={Link} to={'/'} value={'/'} label={'Home'} icon={<Home/>} />
        <BottomNavigationAction component={Link} to={'/another-route'} value={'/another-route'} label={'Favourites'} icon={<Favorite/>} />
    </BottomNavigation>
)

This means the initial state for value is always taken from the current URL.

like image 28
nihilok Avatar answered Oct 23 '22 09:10

nihilok