Material-UI's Tabs integration with react router 4?

后端 未结 9 2053
一个人的身影
一个人的身影 2020-12-24 10:58

The new react-router syntax uses the Link component to move around the routes. But how could this be integrated with material-ui?

In my cas

相关标签:
9条回答
  • 2020-12-24 11:12

    My instructor helped me with using React Router 4.0's withRouter to wrap the Tabs component to enable history methods like so:

    import React, {Component} from "react";
    import {Tabs, Tab} from 'material-ui';
    import { withRouter } from "react-router-dom";
    
    import Home from "./Home";
    import Portfolio from "./Portfolio";
    
    class NavTabs extends Component {
    
     handleCallToRouter = (value) => {
       this.props.history.push(value);
     }
    
      render () {
         return (
          <Tabs
            value={this.props.history.location.pathname}
            onChange={this.handleCallToRouter}
            >
            <Tab
              label="Home"
              value="/"
            >
            <div>
               <Home />
            </div>
            </Tab>
            <Tab
              label="Portfolio"
              value="/portfolio"
                >
              <div>
                <Portfolio />
              </div>
            </Tab>
          </Tabs>           
        )
      }
    }
    
    export default withRouter(NavTabs)  
    

    Simply add BrowserRouter to index.js and you're good to go.

    0 讨论(0)
  • 2020-12-24 11:13

    I got it working this way in my app:

    import React, {useEffect, useRef} 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 Container from "@material-ui/core/Container";
    import {Link} from "react-router-dom";
    import MenuIcon from "@material-ui/icons/Menu";
    import VideoCallIcon from "@material-ui/icons/VideoCall";
    
    const docStyles = makeStyles(theme => ({
        root: {
            display: 'flex',
            '& > * + *': {
                marginLeft: theme.spacing(2),
            },
        },
        appBarRoot: {
            flexGrow: 1,
        },
        headline: {
            marginTop: theme.spacing(2),
        },
        bodyCopy: {
            marginTop: theme.spacing(1),
            fontSize: '1.2rem',
        },
        tabContents: {
            margin: theme.spacing(3),
        },
    }));
    
    function TabPanel(props) {
        const {children, value, index, classes, ...other} = props;
    
        return (
            <div
                role="tabpanel"
                hidden={value !== index}
                id={`simple-tabpanel-${index}`}
                aria-labelledby={`simple-tab-${index}`}
                {...other}
            >
                {value === index && (
                    <Container>
                        <Box className={classes.tabContents}>
                            {children}
                        </Box>
                    </Container>
                )}
            </div>
        );
    }
    
    function a11yProps(index) {
        return {
            id: `simple-tab-${index}`,
            'aria-controls': `simple-tabpanel-${index}`,
        };
    }
    
    function TabOneContents(props) {
        const {classes} = props;
        return (
            <>
                <Typography variant="h4" component={'h1'} className={classes.headline}>
                    Headline 1
                </Typography>
    
                <Typography variant="body1" className={classes.bodyCopy}>
                    Body Copy 1
                </Typography>
            </>
        )
    }
    
    function TabTwoContents(props) {
        const {classes} = props;
        const nurseOnboardingPath = '/navigator/onboarding/' + Meteor.userId() + '/1';
    
        return (
            <>
                <Typography variant="h4" component={'h1'} className={classes.headline}>
                    Headline 2
                </Typography>
    
                <Typography variant="body1" className={classes.bodyCopy}>
                    Body Copy 2
                </Typography>
            </>
        )
    }
    
    export default function MUITabPlusReactRouterDemo(props) {
        const {history, match} = props;
        const propsForDynamicClasses = {};
        const classes = docStyles(propsForDynamicClasses);
        const [value, setValue] = React.useState(history.location.pathname.includes('/tab_2') ? 1 : 0);
    
        const handleChange = (event, newValue) => {
            setValue(newValue);
            const pathName = '/' + (value == 0 ? 'tab_1' : 'tab_2');
            history.push(pathName);
        };
    
    
        return (
            <div className={classes.appBarRoot}>
                <AppBar position="static" color="transparent">
                    <Tabs value={value} onChange={handleChange} aria-label="How It Works" textColor="primary">
                        <Tab label="Tab 1" {...a11yProps(0)} />
                        <Tab label="Tab 2" {...a11yProps(1)} />
                    </Tabs>
                </AppBar>
                <TabPanel value={value} index={0} classes={classes}>
                    <TabOneContents classes={classes}/>
                </TabPanel>
                <TabPanel value={value} index={1} classes={classes}>
                    <TabTwoContents classes={classes}/>
                </TabPanel>
            </div>
        );
    }
    

    ...and in React Router:

    [.....]
    <Route exact path="/tab_1"
           render={(routeProps) =>
               <MUITabPlusReactRouterDemo history={routeProps.history}
               />
           }/>
    
    <Route exact path="/tab_2"
           render={(routeProps) =>
               <MUITabPlusReactRouterDemo history={routeProps.history}                           />
           }/>
    [.....]
    
    0 讨论(0)
  • 2020-12-24 11:14

    Another solution (https://codesandbox.io/s/l4yo482pll) with no handlers nor HOCs, just pure react-router and material-ui components:

    import React, { Fragment } from "react";
    import ReactDOM from "react-dom";
    import Tabs from "@material-ui/core/Tabs";
    import Tab from "@material-ui/core/Tab";
    import { Switch, Route, Link, BrowserRouter, Redirect } from "react-router-dom";
    
    function App() {
      const allTabs = ['/', '/tab2', '/tab3'];
    
      return (
        <BrowserRouter>
          <div className="App">
            <Route
              path="/"
              render={({ location }) => (
                <Fragment>
                  <Tabs value={location.pathname}>
                    <Tab label="Item One" value="/" component={Link} to={allTabs[0]} />
                    <Tab label="Item Two" value="/tab2" component={Link} to={allTabs[1]} />
                    <Tab
                      value="/tab3"
                      label="Item Three"
                      component={Link}
                      to={allTabs[2]}
                    />
                  </Tabs>
                  <Switch>
                    <Route path={allTabs[1]} render={() => <div>Tab 2</div>} />
                    <Route path={allTabs[2]} render={() => <div>Tab 3</div>} />
                    <Route path={allTabs[0]} render={() => <div>Tab 1</div>} />
                  </Switch>
                </Fragment>
              )}
            />
          </div>
        </BrowserRouter>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
    0 讨论(0)
  • 2020-12-24 11:20
    import React, { useContext, useEffect } from "react";
    import PropTypes from "prop-types";
    import Drawer from "@material-ui/core/Drawer";
    import IconButton from "@material-ui/core/IconButton";
    import MenuIcon from "@material-ui/icons/Menu";
    import Typography from "@material-ui/core/Typography";
    import useStyles from "./Styles";
    import Tabs from "@material-ui/core/Tabs";
    import Tab from "@material-ui/core/Tab";
    import Box from "@material-ui/core/Box";
    import { __t } from "core/translation/translation";
    import BrowserData from "core/helper/BrowserData";
    import profileMenuItems from "./MenuItems";
    import LayoutContext from "components/layout/core/LayoutContext";
    import { useHistory, useParams } from "react-router-dom";
    
    function TabPanel(props) {
      const { children, value, index, ...other } = props;
      return (
        <div
          role="tabpanel"
          hidden={value !== index}
          id={`vertical-tabpanel-${index}`}
          aria-labelledby={`vertical-tab-${index}`}
          {...other}
        >
          {value === index && (
            <Box p={3}>
              <Typography>{children}</Typography>
            </Box>
          )}
        </div>
      );
    }
    
    TabPanel.propTypes = {
      children: PropTypes.node,
      index: PropTypes.any.isRequired,
      value: PropTypes.any.isRequired,
    };
    
    export default function UserProfile(props) {
      const { window } = props;
      const classes = useStyles();
      const history = useHistory();
      const { page } = useParams();
      const { isDesktop } = useContext(LayoutContext);
      const [open, setOpen] = React.useState(false);
      const [value, setValue] = React.useState(0);
    
      const handleChange = (event, newValue) => {
        setValue(newValue);
        history.push("/yourPath/" + newValue);
      };
    
      useEffect(() => {
        if (!!page) {
          setValue(eval(page));
        }
      }, [page]);
    
    
      const getContent = () => {
        const { component: Component } = MenuItems[value];
        return <Component />;
      };
    
      const handleDrawerToggle = () => {
        setOpen((prevState) => !prevState);
      };
    
      const Menu = (
        <div>
          <Tabs
            orientation="vertical"
            variant="scrollable"
            value={value}
            onChange={handleChange}
            className={classes.tabs}
          >
            {MenuItems.map(
              ({ label, iconPath, iconClassName = "" }, index) => (
                <Tab
                  label={label}
                  id={`vertical-tab-${index}`}
                  aria-controls={`vertical-tabpanel-${index}`}
                  className={classes.tab}
                  icon={
                    <img className={iconClassName} src={iconPath} alt={label} />
                  }
                />
              )
            )}
          </Tabs>
        </div>
      );
    
      return (
        <div className={classes.root}>
          <IconButton
            color="inherit"
            aria-label="open drawer"
            edge="start"
            onClick={handleDrawerToggle}
            className={classes.drawerToggleButton}
          >
            <MenuIcon />
          </IconButton>
    
          <nav className={classes.drawer}>
            <Drawer
              anchor="left"
              open={isDesktop ? true : open}
              onClose={handleDrawerToggle}
              variant={isDesktop ? "permanent" : "temporary"}
              classes={{
                paper: classes.drawerPaper,
              }}
              ModalProps={{
                keepMounted: true,
              }}
            >
              {Menu}
            </Drawer>
          </nav>
    
          <main className={classes.content}>
            <TabPanel
              value={value}
              key={value}
              index={value}
              className={classes.tabPanel}
            >
              {getContent()}
            </TabPanel>
          </main>
        </div>
      );
    }
    
    0 讨论(0)
  • 2020-12-24 11:24

    As @gkatchmar says you can use withRouter high-order component but you can also use context API. Since @gkatchmar showed withRouter already I will only show context API. Bear in mind that this is an experimental API.

    https://stackoverflow.com/a/42716055/3850405

    import React, {Component} from "react";
    import {Tabs, Tab} from 'material-ui';
    import * as PropTypes from "prop-types";
    
    export class NavTabs extends Component {
    constructor(props) {
     super(props);
    }
    
    static contextTypes = {
        router: PropTypes.object
    }
    
    handleChange = (event: any , value: any) => {
        this.context.router.history.push(value);
    };
    
      render () {
         return (
          <Tabs
            value={this.context.router.history.location.pathname}
            onChange={this.handleChange}
            >
            <Tab
              label="Home"
              value="/"
            >
            <div>
               <Home />
            </div>
            </Tab>
            <Tab
              label="Portfolio"
              value="/portfolio"
                >
              <div>
                <Portfolio />
              </div>
            </Tab>
          </Tabs>           
        )
      }
    }
    
    0 讨论(0)
  • 2020-12-24 11:25

    Here's another solution, using the beta of Material 1.0 and adding browser Back/Forward to the mix:

    import React from 'react';
    import PropTypes from 'prop-types';
    import { withStyles } from 'material-ui/styles';
    import AppBar from 'material-ui/AppBar';
    import Tabs, { Tab } from 'material-ui/Tabs';
    import { withRouter } from "react-router-dom";
    import Home from "./Home";
    import Portfolio from "./Portfolio";
    
    function TabContainer(props) {
      return <div style={{ padding: 20 }}>{props.children}</div>;
    }
    
    const styles = theme => ({
      root: {
        flexGrow: 1,
        width: '100%',
        marginTop: theme.spacing.unit * 3,
        backgroundColor: theme.palette.background.paper,
      },
    });
    
    class NavTabs extends React.Component {
      state = {
        value: "/",
      };
    
      componentDidMount() {
        window.onpopstate = ()=> {
          this.setState({
            value: this.props.history.location.pathname
          });
      }
    }
    
      handleChange = (event, value) => {
        this.setState({ value });
        this.props.history.push(value);
      };
    
      render() {
        const { classes } = this.props;
        const { value } = this.state;
    
        return (
          <div className={classes.root}>
            <AppBar position="static" color="default">
              <Tabs
                value={value}
                onChange={this.handleChange}
                scrollable
                scrollButtons="on"
                indicatorColor="primary"
                textColor="primary"
              >
                <Tab label="Home" value = "/" />
                <Tab label="Portfolio" value = "/portfolio"/>
              </Tabs>
            </AppBar>
            {value === "/" && <TabContainer>{<Home />}</TabContainer>}
            {value === "/portfolio" && <TabContainer>{<Portfolio />}</TabContainer>}
          </div>
        );
      }
    }
    
    NavTabs.propTypes = {
      classes: PropTypes.object.isRequired,
    };
    
    export default withRouter(withStyles(styles)(NavTabs));
    
    0 讨论(0)
提交回复
热议问题