Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set Material-UI tab active based on visited route

In my new react-redux app (I'm a newbie to react), I'm using Material-UI and have added material-ui tabs on my web app. The tabs are intended to change the URL upon selection which I achieved using the following piece of code.

function handleActive(tab) {
    browserHistory.push(tab.props['data-route']);
}

I want my tabs to show selected tab indicator when I visit the routes manually instead of clicking on the tabs. Which I'm right now not able to figure out. Can anyone suggest me a way to do that either by react or redux? Let me know, If I'm not clear in my question.

UPDATE

Code in my components render(). There is nothing else in component.

render() {
    function handleActive(tab) {
        browserHistory.push(tab.props['data-route']);
    }

    return (
        <Paper>
            <Toolbar className="navbar">
                <ToolbarGroup>
                    <Link to="/" style={{ textDecoration: 'none' }}>
                        <ToolbarTitle style={this.styles.title} text="Test App" />
                    </Link>
                </ToolbarGroup>
                <ToolbarGroup className="tab-container">
                    <Tabs className="tabs">
                        <Tab data-route="/" onActive={handleActive} className="tab" label="Route A" />
                        <Tab data-route="/routeB" onActive={handleActive} className="tab" label="Route B" />
                        <Tab data-route="/routeC" onActive={handleActive} className="tab" label="Route C" />
                        <Tab data-route="/routeD" onActive={handleActive} className="tab" label="Route D" />
                    </Tabs>
                </ToolbarGroup>
            </Toolbar>
        </Paper>
    );
}
like image 587
Vishal Sharma Avatar asked Sep 17 '16 05:09

Vishal Sharma


3 Answers

I doubt if parsing url is a great idea. Maybe it's better to control tab index from router dispatching action in onEnter prop of <Route>?

Let's say you have 2 routes:

<Route path='sign_in' component={AuthenticationWrapper} onEnter={() => { store.dispatch(changeTab(0)) }} />
<Route path='sign_up' component={AuthenticationWrapper} onEnter={() => { store.dispatch(changeTab(1)) }} />

Action creator:

changeTab = (index) => {
  return { type: AUTH_TAB_CHANGE, index }
}

Reducer:

authTab = (state = 0, action = {}) => {
  switch (action.type) {
    case AUTH_TAB_CHANGE:
      return action.index
    default:
      return state
  }
}

Component:

class AuthenticationWrapper extends Component {
  constructor(props) {
    super(props)

    this.handleSignIn = this.handleActive.bind(this, '/sign_in')
    this.handleSignUp = this.handleActive.bind(this, '/sign_up')
  }

  handleActive(url) {
    browserHistory.push(url)
  }

  render() {
    const { authTab, changeTab } = this.props

    return (
      <Tabs index={authTab} onChange={changeTab}>
        <Tab label='Sign In' onActive={this.handleSignIn}>
          <SignInForm />
        </Tab>
        <Tab label='Sign Up' onActive={this.handleSignUp}>
          <SignUpForm />
        </Tab>
      </Tabs>
    )
  }
}

const mapStateToProps = ({ authTab }) => {
  return {
    authTab
  }
}

export default connect(mapStateToProps, {
  changeTab,
})(AuthenticationWrapper)

Such approach will work both with visiting different urls and changing tabs by clicking on them without refreshing page.

like image 103
ktaras Avatar answered Nov 13 '22 17:11

ktaras


Although there are few ways to do it, Since you're using React Component Class, You can add componentWillMount

componentWillMount() {

  let urlPath = window.location.pathname;
  let currentTab = urlPath.split('/').pop();
  // you can add more validations here
  this.setState({ activeTab: currentTab || 'default' });

}

And in your render method, change <Tabs className="tabs"> to

<Tabs
  className="tabs"
  value={this.state.activeTab}
  >
    <Tab value="default" data-route="/" onActive={handleActive} className="tab" label="Route A" />
    <Tab value="routeC" data-route="/routeC" onActive={handleActive} className="tab" label="Route C" />
    ...

I don't know if this is how we're supposed to set the value for the Tab.., But play around with it.

And it should work.

I have used setState here instead of redux, As there's nothing wrong in using Component state as long as this is the only component which needs to know which tab is currently active., You can easily change it to use redux though.

like image 28
Dhruv Kumar Jha Avatar answered Nov 13 '22 16:11

Dhruv Kumar Jha


To achieve this, you need to do a couple of things to make sure that you can catch url in the page where tabs are in. Here is what you should do:

  • When you render your highest component, make sure you have <Router history={browserHistory} routes={routes} /> rendered in the div id of your index html. It seems you are doing this since you have browserHistory working.
  • In you routes, make sure you have nested routes so that in the parent component, you pass this.props.children so that you have /path/to/comp in the children reflected in the url. Again, you probably have this set up as well.
  • Now, you will need to add value to your Tabs and Tab component as explained in here http://www.material-ui.com/#/components/tabs. Value will specify which Tab is active depending on the one selected for Tabs so <Tabs className="tabs" value={this is where your this.props.params.name_of_changing_route}> or in case you have react-router 2.4.0, you can wrap your component with withRouter then you can have access to params, location and route.
like image 1
Kafo Avatar answered Nov 13 '22 17:11

Kafo