Search code examples
javascriptreactjsmaterial-uisetstatereact-state-management

navigation problem in Tabs provided by Material UI for React js Project


I am using React Js in my project and the Material UI Tabs component to navigate between 2 components.

When I go to Tab 2 or Tab 1 by clicking on it, my component renders and the route gets changed but the tab indicator does not get updated on the active tab.

I've already tried to update the state on the button click but it isn't working

class NavBars extends React.Component {
  state = {
    value: 0,
  };

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

  navigateToTab2 = () => {
    this.setState({ value: 1 });
    console.log(this.state);
    this.props.history.push('/tab2');
  }

  navigateToTab1 = (facility, event) => {
    this.setState({ value: 0 });
    console.log(this.state);
    this.props.history.push('/tab1', {
      facilityId: facility.facilityId,
      facilityName: facility.facilityName,
    });
  }

  render() {
    const { classes } = this.props;
    const { value } = this.state;

    return (
      <div className={classes.root}>
        <Tabs
          value={value}
          onChange={this.handleChange}
          classes={{ root: classes.tabsRoot, indicator: classes.tabsIndicator }}
        >
          <Tab
            disableRipple
            classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
            label="Tab 1"
            onClick={this.navigateToTab1}
          />
          <Tab
            disableRipple
            classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
            label="Tab 2"
            onClick={this.navigateToTab2}
          />
        </Tabs>
      </div>
    );
  }
}

I expect that when I click on Tab1 it should go to Tab1 and render that component with Tab indicator be on Tab 1 and when I click on Tab 2 it should go to Tab 2 and render the Tab 2 component with tab indicator on tab 2 enter image description here


Solution

  • A couple things:

    There's really no point in setting onClick events for each Tab, because everything you need to do can be done in the onChange handler for Tabs.

    So, when onChange is fired, you can set the selected tab value, then call this.props.history.push. Make sure you are rendering your child components based on the tab that was selected.

    If you want the route /tab2 to automatically select tab2, then youll need to check the current route in componentDidMount(), then set state accordingly. So for instance, if route == '/tab2' then state.value == 2

    Here's an example taken straight from Material-UI docs.

    <div className={classes.root}>
      <AppBar position="static">
        <Tabs value={value} onChange={this.handleChange}>
          <Tab label="Item One" />
          <Tab label="Item Two" />
          <Tab label="Item Three" />
        </Tabs>
      </AppBar>
      {value === 0 && <TabContainer>Item One</TabContainer>}
      {value === 1 && <TabContainer>Item Two</TabContainer>}
      {value === 2 && <TabContainer>Item Three</TabContainer>}
    </div>
    

    Your component should end up looking something like this

    class NavBars extends React {
      state = {
        value: 0,
      };
    
      handleChange = (event, value) => {
        if (value == 0) {this.props.history.push('/tab1')}
        if(value == 1) {this.props.history.push('/tab2')}
      };
    
      componentDidMount() {
        if(this.props.location.pathname == '/tab1') {
          this.setState({value: 0})
        } else {
          this.setState({value: 1})
        }
    
      }
    
      render() {
        const { classes } = this.props;
        const { value } = this.state;
    
        return (
          <div className={classes.root}>
            <Tabs
              value={value}
              onChange={this.handleChange}
              classes={{ root: classes.tabsRoot, indicator: classes.tabsIndicator }}
            >
              <Tab
                disableRipple
                classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
                label="Tab 1"
              />
              <Tab
                disableRipple
                classes={{ root: classes.tabRoot, selected: classes.tabSelected }}
                label="Tab 2"
              />
            </Tabs>
    
            {value == 0 && <p>Here is tab 1 content</p>}
            {value == 1 && <p>Here is tab 2 content</p>}
    
          </div>
        );
      }
    }