0
votes

I'm encountering an error when I try to clear the selected tab indicator from my Tabs navigation.

The element contains five individual Tab components in addition to a Button component that reroutes the user to our login screen.

If I click on the Login button, then the console displays the following error: "Material-UI: the value provided 5 to the Tabs component is invalid. None of the Tabs children have this value. You can provide one of the following values: 0, 1, 2, 3, 4."

Clicking on the button reroutes to the proper login path ("/login"), but leaves the previous tab showing as selected. However, if I refresh the page, then the selection indicator disappears as it's supposed to.

Any tips on how to remove this error and clear the selected indicator as soon as the Login button is pressed, rather than having to do a refresh?

    import React, { useState, useEffect, Fragment } from 'react';
    import AppBar from '@material-ui/core/AppBar';
    import Toolbar from '@material-ui/core/Toolbar';
    import useScrollTrigger from '@material-ui/core/useScrollTrigger';
    import { makeStyles } from '@material-ui/styles';
    import Tabs from '@material-ui/core/Tabs';
    import Tab from '@material-ui/core/Tab';
    import Button from '@material-ui/core/Button';
    import { Link } from 'react-router-dom';
    import Menu from '@material-ui/core/Menu';
    import MenuItem from '@material-ui/core/MenuItem';
    import useMediaQuery from '@material-ui/core/useMediaQuery';
    import { useTheme } from '@material-ui/core/styles';
    import SwipeableDrawer from '@material-ui/core/SwipeableDrawer';
    import MenuIcon from '@material-ui/icons/Menu';
    import IconButton from '@material-ui/core/IconButton';
    import List from '@material-ui/core/List';
    import ListItem from '@material-ui/core/ListItem';
    import ListItemText from '@material-ui/core/ListItemText';


    import logo from '../../assets/logo.png';


    function ElevationScroll(props) {
      const { children, window } = props;

      const trigger = useScrollTrigger({
        disableHysteresis: true,
        threshold: 0,
      });

      return React.cloneElement(children, {
        elevation: trigger ? 4 : 4,
      });
    }

    const useStyles = makeStyles(theme => ({
      toolbarMargin: {
        ...theme.mixins.toolbar,
        marginBottom: "1rem",
        [theme.breakpoints.down("md")]: {
          marginBottom: ".5rem",
          transition: "all .2s ease-out"
        },
        [theme.breakpoints.down("xs")]: {
          marginBottom: ".25rem",
          transition: "all .2s ease-out"
        }
      },
      logo: {
        height: "2.5rem",
        margin: "1rem 0 1rem 2rem",
        transition: "all .2s ease-out",
        [theme.breakpoints.down("md")]: {
          height: "2rem",
          transition: "all .2s ease-out",
          marginLeft: "1.5rem"
        },
        [theme.breakpoints.down("xs")]: {
          marginLeft: "1rem",
          height: "1.5rem",
          transition: "all .2s ease-out"
        }
      },
      logoContainer: {
        padding: 0,
        backgroundColor: "transparent"
      },
      tabContainer: {
        marginLeft: "auto"
      },
      tab: {
        ...theme.typography.tab,
        minWidth: "1rem",
        marginLeft: "2rem",
        transition: "all .2s ease-out",
        "&:hover": {
          opacity: 1,
          transition: "all .3s ease-out",
          borderRadius: "3px",
        }
      },
      button: {
        margin: "0 3rem 0 1.5rem",
        padding: "0 1.5rem",
        height: "2.5rem"
      },
      styledIndicator: {
        backgroundColor: theme.palette.primary.light
      },
      menu: {
        backgroundColor: theme.palette.secondary.dark,
        color: theme.palette.background.light,
        borderRadius: "0px"
      },
      menuItem: {
        ...theme.typography.tab,
        opacity: 0.7,
        "&:hover": {
          opacity: 1,
          backgroundColor: theme.palette.common.black
        }
      },
      drawerIcon: {
        height: "2rem",
        width: "2rem"
      },
      drawerIconContainer: {
        marginLeft: "auto",
        "&:hover": {
          backgroundColor: "transparent"
        }
      },
      drawer: {
        backgroundColor: theme.palette.primary.dark
      },
      drawerItem: {
        ...theme.typography.button,
        color: theme.palette.background.light,
        paddingRight: "3.5rem",
        opacity: .85
      },
      drawerItemLogin: {
        ...theme.typography.button,
        backgroundColor: theme.palette.secondary.dark,
        transition: "all .2s ease-out",
        "&:hover": {
          backgroundColor: theme.palette.common.black
        },
        "&.Mui-selected": {
          backgroundColor: theme.palette.common.black,
          "&:hover": {
            backgroundColor: theme.palette.common.black
          },
        }
      },
      drawerItemSelected: {
        opacity: 1
      }
    }))

    export default function Header(props) {
      const classes = useStyles();
      const theme = useTheme();
      const iOS = process.browser && /iPad|iPhone|iPod/.test(navigator.userAgent);
      const matches = useMediaQuery(theme.breakpoints.down("md"));

      const [openDrawer, setOpenDrawer] = useState(false);
      const [value, setValue] = useState(0);
      const [anchorEl, setAnchorEl] = useState(null);
      const [openMenu, setOpenMenu] = useState(false);
      const [selectedIndex, setSelectedIndex] = useState(0);

      const handleChange = (e, newValue) => {
        setValue(newValue);
      }

      const handleClick = (e) => {
        setAnchorEl(e.currentTarget);
        setOpenMenu(true);
      }

      const handleMenuItemClick = (e, idx) => {
        setAnchorEl(null);
        setOpenMenu(false);
        setSelectedIndex(idx)
      }

      const handleClose = (e) => {
        setAnchorEl(null);
        setOpenMenu(false);
      }

      const menuOptions = [
        {
          name: "Brands",
          link: "/brands",
        }, {
          name: "Overview",
          link: "/overview",
        }, {
          name: "Use Cases",
          link: "/usecases",
        }, {
          name: "Pricing",
          link: "/pricing",
        }
      ]

      useEffect(() => {
        switch (window.location.pathname) {
          case "/":
            if (value !== 0) {
              setValue(0)
            }
            break;
          case "/artists":
            if (value !== 1) {
              setValue(1)
            }
            break;
          case "/brands":
            if (value !== 2) {
              setValue(2)
              setSelectedIndex(0)
            }
            break;
          case "/overview":
            if (value !== 2) {
              setValue(2)
              setSelectedIndex(1)
            }
            break;
          case "/usecases":
            if (value !== 2) {
              setValue(2)
              setSelectedIndex(2)
            }
            break;
          case "/pricing":
            if (value !== 2) {
              setValue(2)
              setSelectedIndex(3)
            }
            break;
          case "/about":
            if (value !== 3) {
              setValue(3)
            }
            break;
          case "/contact":
            if (value !== 4) {
              setValue(4)
            }
            break;
          case "/login":
            if (value !== 5) {
              setValue(5);
              // setAnchorEl(null)
            }
            break;
          default:
            break
        }
      })

      const tabs = (
        <Fragment>
          <Tabs
            value={value}
            onChange={handleChange}
            className={classes.tabContainer}
            classes={{ indicator: classes.styledIndicator }}
            indicatorColor="#007EBB"
          >
            <Tab className={classes.tab} label="Home" component={Link} to={"/"} />
            <Tab className={classes.tab} label="Artists" component={Link} to={"/artists"} />
            <Tab
              className={classes.tab}
              label="Brands"
              component={Link}
              onMouseOver={e => handleClick(e)}
              to={"/brands"}
            />
            <Tab className={classes.tab} label="About" component={Link} to={"/about"} />
            <Tab className={classes.tab} label="Contact" component={Link} to={"/contact"} />
          </Tabs>
          <Button className={classes.button} variant="contained" color="secondary" component={Link} to={"/login"}>
            Login
          </Button>
          <Menu
            anchorEl={anchorEl}
            open={openMenu}
            onClose={handleClose}
            MenuListProps={{ onMouseLeave: handleClose }}
            classes={{ paper: classes.menu }}
          >
            {menuOptions.map((option, idx) => (
              <MenuItem
                key={option}
                component={Link}
                to={option.link}
                classes={{ root: classes.menuItem }}
                onClick={(e) => {
                  handleMenuItemClick(e, idx);
                  setValue(2);
                  handleClose();
                }}
                selected={idx === selectedIndex && value === 1}
              >
                {option.name}
              </MenuItem>
            ))}
          </Menu>
        </Fragment>
      )

      const drawer = (
        <Fragment>
          <SwipeableDrawer
            disableBackdropTransition={!iOS}
            disableDiscovery={iOS}
            open={openDrawer}
            onClose={() => setOpenDrawer(false)}
            onOpen={() => setOpenDrawer(true)}
            classes={{ paper: classes.drawer }}
          >
            <List disablePadding>
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 0} component={Link} to="/">
                <ListItemText className={value === 0 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>Home</ListItemText>
              </ListItem>
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 1} component={Link} to="/artists">
                <ListItemText className={value === 1 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>Artists</ListItemText>
              </ListItem>
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 2} component={Link} to="/brands">
                <ListItemText className={value === 2 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>Brands</ListItemText>
              </ListItem>
              {/* <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 2} component={Link} to="/overview">
                <ListItemText className={value === 2 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>Overview</ListItemText>
              </ListItem>
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 2} component={Link} to="/usecases">
                <ListItemText className={value === 2 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>Use Cases</ListItemText>
              </ListItem>
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 2} component={Link} to="/pricing">
                <ListItemText className={value === 2 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>Pricing</ListItemText>
              </ListItem> */}
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 3} component={Link} to="/about">
                <ListItemText className={value === 3 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>About</ListItemText>
              </ListItem>
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} divider button selected={value === 4} component={Link} to="/contact">
                <ListItemText className={value === 4 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem} disableTypography>Contact</ListItemText>
              </ListItem>
              <ListItem onClick={() => { setOpenDrawer(false); setValue(0) }} className={classes.drawerItemLogin} divider button selected={value === 5} component={Link} to="/login">
                <ListItemText
                  className={value === 5 ? [classes.drawerItem, classes.drawerItemSelected] : classes.drawerItem}
                  disableTypography
                >Login</ListItemText>
              </ListItem>
            </List>
          </SwipeableDrawer>
          <IconButton
            className={classes.drawerIconContainer}
            onClick={() => setOpenDrawer(!openDrawer)}
            disableRipple>
            <MenuIcon className={classes.drawerIcon} />
          </IconButton>
        </Fragment >
      )

      return (
        <React.Fragment>
          <ElevationScroll>
            <AppBar position="fixed" color="primary">
              <Toolbar disableGutters={true}>
                <Button
                  component={Link}
                  to="/"
                  disableRipple
                  className={classes.logoContainer}
                  onClick={() => setValue(0)}
                >
                  <img src={logo} alt="Cuttime Logo" className={classes.logo} />
                </Button>
                {matches ? drawer : tabs}
              </Toolbar>
            </AppBar>
          </ElevationScroll>
          <div className={classes.toolbarMargin} />
        </React.Fragment >
      );
    }
1

1 Answers

2
votes

If you don't want any tab selected when on the login page, then you should set the value to false rather than an invalid value (e.g. 5).

From the documentation of the value prop for Tabs (https://material-ui.com/api/tabs/#props):

The value of the currently selected Tab. If you don't want any selected Tab, you can set this property to false.

Below is an example demonstrating this:

import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";
import Button from "@material-ui/core/Button";

function TabPanel(props) {
  const { children, value, index, ...other } = props;

  return (
    <Typography
      component="div"
      role="tabpanel"
      hidden={value !== index}
      id={`simple-tabpanel-${index}`}
      aria-labelledby={`simple-tab-${index}`}
      {...other}
    >
      {value === index && <Box p={3}>{children}</Box>}
    </Typography>
  );
}

TabPanel.propTypes = {
  children: PropTypes.node,
  index: PropTypes.any.isRequired,
  value: PropTypes.any.isRequired
};

function a11yProps(index) {
  return {
    id: `simple-tab-${index}`,
    "aria-controls": `simple-tabpanel-${index}`
  };
}

const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1,
    backgroundColor: theme.palette.background.paper
  }
}));

export default function SimpleTabs() {
  const classes = useStyles();
  const [value, setValue] = React.useState(false);

  const handleChange = (event, newValue) => {
    setValue(newValue);
  };

  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Tabs
          value={value}
          onChange={handleChange}
          aria-label="simple tabs example"
        >
          <Tab label="Item One" {...a11yProps(0)} />
          <Tab label="Item Two" {...a11yProps(1)} />
          <Tab label="Item Three" {...a11yProps(2)} />
        </Tabs>
      </AppBar>
      <TabPanel value={value} index={0}>
        Item One
      </TabPanel>
      <TabPanel value={value} index={1}>
        Item Two
      </TabPanel>
      <TabPanel value={value} index={2}>
        Item Three
      </TabPanel>
      {value !== false && (
        <Button onClick={() => setValue(false)}>De-select tab</Button>
      )}
    </div>
  );
}

Edit De-select tab