Material-ui 从 react-router 添加 Link 组件
Posted
技术标签:
【中文标题】Material-ui 从 react-router 添加 Link 组件【英文标题】:Material-ui adding Link component from react-router 【发布时间】:2016-10-17 01:03:52 【问题描述】:我正在努力将<Link/>
组件添加到我的material-ui AppBar
这是我的导航类:
class Navigation extends Component
constructor(props)
super(props)
render()
var styles =
appBar:
flexWrap: 'wrap'
,
tabs:
width: '100%'
return (
<AppBar showMenuIconButton=false style=styles.appBar>
<Tabs style=styles.tabs>
<Tab label='Most popular ideas'/>
<Tab label='Latest ideas' />
<Tab label='My ideas' />
</Tabs>
</AppBar>
)
看起来不错:
标签是可点击的,有流畅的动画,这很酷。但是如何将它们与react-router
及其'<Link/>
组件连接在一起?
我试过像这样添加onChange
监听器:
<Tab
label='My ideas'
onChange=<Link to='/myPath'></Link>
/>
但是我收到以下错误:
Uncaught Invariant Violation: Expected onChange listener to be a function, instead got type object
如果我尝试将 <Tab/>
组件包装到 <Link/>
组件中,我会收到 <Tabs/>
组件仅接受 <Tab/>
组件的错误。
这也不起作用(没有产生错误,但单击 Tab 不会将我带到路径):
<Tab label='Most popular ideas'>
<Link to='/popular'/>
</Tab>
如何使<Link/>
组件与<Tabs>
和<AppBar>
一起工作?如果这不可能,我可以使用 material-ui
库中的任何其他组件来形成正确的菜单。
【问题讨论】:
【参考方案1】:这似乎对我有用
import Link as RouterLink from 'react-router-dom';
import Link from '@mui/material/Link';
<Link to=menuItem.url component=RouterLink aria-current="page">
menuItem.label
</Link>
【讨论】:
【参考方案2】:使用href=""
选项,如下所示:
<Tab
href="/en/getting-started"
label="Contact US"
style= color: "white", textDecoration: "none"
/>
要消除点击时的涟漪效应,请使用选项disableRipple
【讨论】:
【参考方案3】:如果你使用 NextJs,你可以这样做,并创建你自己的组件。
*我没有用'a'标签包裹标签,因为它是自动添加的
const WrapTab = (props) =>
const href = props
return (
<Link href=href style= width: "100%" >
<Tab ...props />
</Link>
)
这就是你的回报
<Tabs
value=value
indicatorColor="primary"
textColor="primary"
onChange=handleChange
variant="fullWidth"
>
<WrapTab href="/testPage/?tab=0" icon=<MenuIcon /> />
<WrapTab href="/testPage/?tab=1" icon=<StampIcon2 /> />
<WrapTab href="/testPage/?tab=2" icon=<ShopIcon /> />
<WrapTab href="/testPage/?tab=3" icon=<PenIcon /> />
<WrapTab href="/testPage/?tab=4" icon=<ProfileIcon /> />
</Tabs>
material-ui 文档的链接: https://material-ui.com/guides/composition/
【讨论】:
【参考方案4】:对于希望使用 Next.js 链接包装 Material-ui 链接的任何人
import Link from "next/link"
import MuiLink from "@material-ui/core/Link"
const CustomNextLink = (href, alt) => (children, ...rest) => (
<Link href=href alt=alt>
<MuiLink ...rest>
children
</MuiLink>
</Link>)
然后将它传递给你的 Tab 组件
<Tab
key=...
label=title
icon=icon
component=CustomNextLink(href: to, alt: title)
style=...
className=...
classes=selected: ...
...a11yProps(index)
/>
【讨论】:
【参考方案5】:这是 React 的另一种实现,包括 hooks、Material-UI 和选项卡、React Router 和 Link 和 TypeScript。
import * as React from "react";
import BrowserRouter as Router, Route, Redirect, Switch, Link, LinkProps from 'react-router-dom';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import default as Tab, TabProps from '@material-ui/core/Tab';
import Home from './Home';
import ProductManagement from './ProductManagement';
import Development from './Development';
import HomeIcon from '@material-ui/icons/Home';
import CodeIcon from '@material-ui/icons/Code';
import TimelineIcon from '@material-ui/icons/Timeline';
const LinkTab: React.ComponentType<TabProps & LinkProps> = Tab as React.ComponentType<TabProps & LinkProps>;
function NavBar()
const [value, setValue] = React.useState(0);
const handleChange = (event: React.ChangeEvent<>, newValue: number) =>
setValue(newValue);
;
return (
<div >
<AppBar position="static" >
<Tabs value=value onChange=handleChange centered>
<LinkTab label='Home' icon= <HomeIcon /> component=Link to="/" />
<LinkTab label='Development' icon=<CodeIcon /> component=Link to="/dev" />
<LinkTab label='Product Management' icon=<TimelineIcon /> component=Link to="/pm" />
</Tabs>
</AppBar>
</div>
)
;
export default function App()
return (
<Router>
<div>
<NavBar />
<Switch>
<Route exact path="/" component= Home />
<Route exact path="/dev" component= Development />
<Route exact path="/pm" component= ProductManagement />
<Redirect from="/" to="/" />
</Switch>
</div>
</Router>
)
【讨论】:
看起来不错。有一个问题 - 如何设置选项卡的初始状态?您使用“0”,所以它是“/home”的路径,但是如果用户将以“/dev”路径开头怎么办?【参考方案6】:检查此链接,我实施了解决方案并为我工作
Composition in material UI
【讨论】:
【参考方案7】:这可以使用 material-ui 中的 <Link />
解决,而不是直接使用 react-router 中的 <Link />
或 <NavLink />
。可以在此处的文档中找到相同的示例。
https://material-ui.com/components/links/
另外<Button />
标签有一个组件prop来实现这个
<Button color="inherit" component=Link to="/logout">Logout</Button>
可以在这里找到关于此的广泛讨论
https://github.com/mui-org/material-ui/issues/850
【讨论】:
【参考方案8】:由于我们使用的是 TypeScript,我无法使用 @hazardous 解决方案。这就是我们为material-ui v1.0.0-beta.16
和react-router 4.2.0
实现路由的方式。我们之所以拆分this.props.history.location.pathname
,是因为例如我们需要访问/renewals/123
。如果我们不这样做,我们会收到以下警告,并且不会显示任何选项卡处于活动状态:Warning: Material-UI: the value provided '/renewals/123' is invalid
带有导入的完整代码:
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as ReactRouter from "react-router";
import * as PropTypes from "prop-types";
import Switch, Route, Redirect, Link from "react-router-dom";
import Cases from './../Cases';
import SidePane from './../SidePane';
import withStyles, WithStyles from 'material-ui/styles';
import Paper from 'material-ui/Paper';
import Tabs, Tab from 'material-ui/Tabs';
import withRouter from "react-router-dom";
import Badge from 'material-ui/Badge';
import Grid from 'material-ui/Grid';
import Theme from 'material-ui/styles';
import SimpleLineIcons from '../../Shared/SimpleLineIcons'
interface IState
userName: string;
interface IProps
history?: any
const styles = (theme: Theme) => (
root: theme.typography.display1,
badge:
right: '-28px',
color: theme.palette.common.white,
,
imageStyle:
float: 'left',
height: '40px',
paddingTop: '10px'
,
myAccount:
float: 'right'
,
topMenuAccount:
marginLeft: '0.5em',
cursor: 'pointer'
);
type WithStyleProps = 'root' | 'badge' | 'imageStyle' | 'myAccount' | 'topMenuAccount';
class Menu extends React.Component<IProps & WithStyles<WithStyleProps>, IState>
constructor(props: IProps & WithStyles<WithStyleProps>)
super(props);
this.state =
userName: localStorage.userName ? 'userName ' + localStorage.userName : ""
componentDidMount()
this.setState( userName: localStorage.userName ? localStorage.userName : "" )
logout(event: any)
localStorage.removeItem('token');
window.location.href = "/"
handleChange = (event: any, value: any) =>
this.props.history.push(value);
;
render()
const classes = this.props.classes;
let route = '/' + this.props.history.location.pathname.split('/')[1];
return (
<div>
<Grid container spacing=24>
<Grid item xs=12 className=classes.root>
<img src="/Features/Client/Menu/logo.png" className=classes.imageStyle />
<div className=this.props.classes.myAccount>
<span><span className=this.props.classes.topMenuAccount>MY ACCOUNT</span><span className=classes.topMenuAccount><SimpleLineIcons iconName='user' />▾</span></span>
<span onClick=this.logout className=classes.topMenuAccount><SimpleLineIcons iconName='logout' /></span>
</div>
</Grid>
<Grid item xs=12 >
<div className="route-list">
<Tabs
value=route
onChange=this.handleChange
indicatorColor="primary"
textColor="primary"
>
<Tab label="Overview" value="/" />
<Tab label=<Badge classes= badge: classes.badge badgeContent=this.props.caseRenewalCount color="primary">
Renewals
</Badge> value="/renewals" />
</Tabs>
</div>
</Grid>
</Grid>
</div>
);
export default withStyles(styles)(withRouter(Menu))
【讨论】:
【参考方案9】:TypeScript 实现路由器驱动的选项卡。
对于那些寻找 TypeScript 实现的人。易于配置。由tabs
配置驱动。
interface ITabsPageProps
match: match<page: string>;
history: History;
const tabs = [
label: 'Fist Tab',
link: 'fist-tab',
component: <FirstTabContent/>
,
label: 'Second Tab',
link: 'second-tab',
component: <SecondTabContent/>
,
label: 'Third Tab',
link: 'third-tab',
component: <ThirdTabContent/>
];
export class TabsPage extends React.Component<ITabsPageProps>
handleChange(tabLink: string)
this.props.history.push(`/tabs-page/$tabLink`);
render()
const currentTab = this.props.match.params.page;
const selectedTab = tabs.find(tab => currentTab === tab.link);
return (
<Fragment>
<Tabs
value=currentTab
onChange=(event, value) => this.handleChange(value)
>
tabs.map(tab => (
<Tab
key=tab.link
value=tab.link
label=tab.label
/>
))
</Tabs>
selectedTab && selectedTab.component
</Fragment>
);
【讨论】:
【参考方案10】:你可以试试这个简单的方法
<Tab label='Most popular ideas' to='/myPath' component=Link />
【讨论】:
这对我有用。但是,无论如何要删除“蓝色下划线”链接样式? 这应该是最新材料-ui 4+的正确答案 2020 年仍然是一个更简单的解决方案 为我抛出Error: React.Children.only expected to receive a single React element child.
没有抛出错误,但它对我不起作用。运行 4.11 核心用户界面【参考方案11】:
对于带有 Typescript 的 Material UI 1.0:请参阅下面的 @ogglas 的 this post。
对于带有纯 JS 的 Material-UI 1.0:
<Tabs value=value onChange=this.handleChange>
this.props.tabs.map(
(label, path)=><Tab key=label
label=label
className=classes.tabLink
component=Link
to=path />
)
</Tabs>
而classes.tabLink
定义为:
tabLink :
display:"flex",
alignItems:"center",
justifyContent:"center"
这是如何工作的?
所有继承自ButtonBase
的mui 1.0 组件,都支持component
属性,参见ButtonBase。这个想法是允许您控制组件作为其包装器/根元素呈现的内容。 Tab
也有这个功能,虽然在写这个答案的时候这个道具没有明确记录,但是因为Tab
继承自ButtonBase
,它的所有道具都继承了(并且文档确实覆盖这个)。
ButtonBase
的另一个特点是所有额外的道具,ButtonBase
或继承的组件没有使用,都分布在指定的component
上。我们已经使用这种行为来发送Link
使用的to
属性,方法是将其交给Tab
控制。您可以以相同的方式发送任何其他道具。请注意,ButtonBase
和 Tab
都明确记录了这一点。
感谢 @josh-l 要求添加此内容。
【讨论】:
您能否更具体地了解为“组件”和“到”API 传递的参数是什么?我正在查看材料 UI v1 api 选项卡文档,但它没有显示其中任何一个? material-ui-1dab0.firebaseapp.com/api/tab 对于大多数 mui 容器组件,您可以发送一个component
属性,使其使用不同的 React 组件而不是默认组件。在这个例子中,我们让 Tab 渲染反应路由器的 Link 控件。在这种模式下,mui 组件会将任何额外的道具传递给该组件。由于 Link 需要 to
道具,我在 Tab 中传递了它。你会发现这种行为记录在 mui 的某个地方。将尽快更新我的答案。
非常感谢!得到它的工作!但是,是的,也许用更多细节更新答案可以帮助其他人。无论哪种方式都赞成!【参考方案12】:
您现在可以这样做:
<Tabs onChange=this.changeTab value=value>
<Tab value=0 label="first" containerElement=<Link to="/first"/> />
<Tab value=1 label="second" containerElement=<Link to="/second"/>/>
<Tab value=2 label="third" containerElement=<Link to="/third"/> />
</Tabs>
【讨论】:
甜蜜。但我注意到文档没有这样说。是否所有元素都支持containerElement
?
我尝试过的那些(他们拥有它是有道理的)他们确实拥有它
在撰写本文时,这种技术似乎不适用于material-ui@next
。【参考方案13】:
所以我对这个解决方案的解决方案非常可靠,尽管它可能比您想要做的解决方案更手动。
我一直在使用的策略是实际上甚至不使用链接组件。相反,您将使用 Tabs onChange 属性作为回调,该回调可以响应 Tab 点击,并使用 Parent 上的 Props 手动跟踪位置。
您可以从 react-router 导入一个名为 History 的实用程序,该实用程序允许您手动推送位置。在使用 React-Router 时,您的组件树将可以访问 Location 属性,该属性的路径名键带有您当前位置的字符串。
我们将手动将此字符串解析为构成您当前 URL 的组件,然后使用 Switch 语句来决定当前选择了哪个选项卡以及单击选项卡时链接到的位置。 (这使您可以对导航进行相当程度的控制)
(例如 ['', 'latest'])
这是您的组件在集成此解决方案后的样子的模型。
import React from 'react';
import History from 'react-router';
function parseLocation(location)
if (String(location))
var locationArray = location.split('/');
return locationArray;
else
return false;
;
function filterPath(path)
let locationArray = parseLocation(path);
return locationArray[locationArray.length - 1];
;
var Navigation = React.createClass(
mixins: [History],
getPage()
if (this.props.location.pathname)
let pathname = this.props.location.pathname;
let pageName = filterPath(pathname);
return pageName;
else
return false;
,
decideContent()
let page = this.getPage();
let content;
switch(page)
case 'popular':
content = 0;
case 'latest':
content = 1;
case 'myideas':
content = 2;
default:
content = 0;
return content;
,
handleTabChange(value)
let location = false;
switch (value)
case 0:
location = 'popular';
break;
case 1:
location = 'latest';
break;
case 2:
location = 'myideas';
break;
if (location && location !== this.getPage())
this.history.pushState(null, '/'+location);
,
render()
var styles =
appBar:
flexWrap: 'wrap'
,
tabs:
width: '100%'
;
let content = this.decideContent();
let tabs = <Tabs
onChange=this.handleTabChange
value=content
>
<Tab label="Most Popular Ideas" value=0 />
<Tab label="Latest Ideas" value=1 />
<Tab label="My Ideas" value=2 />
</Tabs>;
return (
<AppBar showMenuIconButton=false style=styles.appBar>
tabs
</AppBar>
);
);
【讨论】:
这对我来说似乎没问题。我会尝试将其添加到我的代码中。以上是关于Material-ui 从 react-router 添加 Link 组件的主要内容,如果未能解决你的问题,请参考以下文章
React-Router 和 Material-UI:根据路由应用自定义主题
React、Redux、React-Router - 卡在安装 material-ui
在 Typescript 中将 React-Router Link 作为组件添加到 Material-UI?