Material-UI's Tabs integration with react router 4?
新的react-router语法使用
就我而言,我将标签用作主要的导航系统,因此从理论上讲,我应该具有以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const TabLink = ({ onClick, href, isActive, label }) => <Tab label={label} onActive={onClick} /> export default class NavBar extends React.Component { render () { return ( <Tabs> <Link to="/">{params => <TabLink label="Home" {...params}/>}</Link> <Link to="/shop">{params => <TabLink label="shop" {...params}/>}</Link> <Link to="/gallery">{params => <TabLink label="gallery" {...params}/>}</Link> </Tabs> ) } } |
但是在渲染时,material-ui会引发错误,指出
预先感谢
另一种解决方案(https://codesandbox.io/s/l4yo482pll),既没有处理程序也没有HOC,仅是纯react-router和material-ui组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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> <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={() => Tab 2} /> <Route path={allTabs[2]} render={() => Tab 3} /> <Route path={allTabs[0]} render={() => Tab 1} /> </Switch> </Fragment> )} /> </BrowserRouter> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); |
我的老师帮助我使用了React Router 4.0的withRouter来包装Tabs组件以启用类似这样的历史记录方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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="/" > <Home /> </Tab> <Tab label="Portfolio" value="/portfolio" > <Portfolio /> </Tab> </Tabs> ) } } export default withRouter(NavTabs) |
只需将BrowserRouter添加到index.js,您就可以进行了。
您从material-ui中看到的错误是因为它希望将
现在,这是我发现的一种将链接集成到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import React, {Component} from 'react'; import {Tabs, Tab} from 'material-ui/Tabs'; import {Link} from 'react-router-dom'; export default class MyComponent extends Component { render() { const {location} = this.props; const {pathname} = location; return ( <Tabs value={pathname}> <Tab label="First tab" containerElement={<Link to="/my-firs-tab-view" />} value="/my-firs-tab-view"> {/* insert your component to be rendered inside the tab here */} </Tab> <Tab label="Second tab" containerElement={<Link to="/my-second-tab-view" />} value="/my-second-tab-view"> {/* insert your component to be rendered inside the tab here */} </Tab> </Tabs> ); } } |
要管理选项卡的'active'属性,可以在
这是另一种解决方案,使用Material 1.0的beta版并将浏览器"前进/后退"添加到组合中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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 {props.children}; } 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 ( <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>} ); } } NavTabs.propTypes = { classes: PropTypes.object.isRequired, }; export default withRouter(withStyles(styles)(NavTabs)); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <BrowserRouter> <AppBar position="static" color="default"> <Tabs value={this.state.value} onChange={this.handleChange} indicatorColor="primary" textColor="primary" fullWidth > <Tab label="Item One" component={Link} to="/one" /> <Tab label="Item Two" component={Link} to="/two" /> </Tabs> </AppBar> <Switch> <Route path="/one" component={PageShell(ItemOne)} /> <Route path="/two" component={PageShell(ItemTwo)} /> </Switch> |
正如@gkatchmar所说,您可以使用
https://stackoverflow.com/a/42716055/3850405
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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="/" > <Home /> </Tab> <Tab label="Portfolio" value="/portfolio" > <Portfolio /> </Tab> </Tabs> ) } } |
您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import { browserHistory } from 'react-router' // Go to /some/path. onClick(label) { browserHistory.push('/${label}'); } // Example for Go back //browserHistory.goBack() <Tabs> <Tab label={label} onActive={() => onClick(label)} /> </Tabs> |
如您所见,您只需将目标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 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> )} ); } 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 = ( <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> ); return ( <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> ); } |
我在我的应用中以这种方式运行它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 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> )} ); } 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 ( <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> ); } |
...并在React Router中:
1 2 3 4 5 6 7 8 9 10 11 12 | [.....] <Route exact path="/tab_1" render={(routeProps) => <MUITabPlusReactRouterDemo history={routeProps.history} /> }/> <Route exact path="/tab_2" render={(routeProps) => <MUITabPlusReactRouterDemo history={routeProps.history} /> }/> [.....] |