Javascript Material-ui 从 react-router 添加 Link 组件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37843495/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Material-ui adding Link component from react-router
提问by Evaldas Buinauskas
I'm struggling to add <Link/>
component to my material-ui AppBar
我正在努力向<Link/>
我的 material-ui AppBar添加组件
This is my navigation class:
这是我的导航课:
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>
)
}
}
Tabs are clickable, have fluid animations, that's cool. But how do I wire them up together with react-router
and its' <Link/>
component?
标签是可点击的,有流畅的动画,这很酷。但是我如何将它们react-router
与它的<Link/>
组件连接起来呢?
I've tried adding onChange
listener like that:
我试过添加这样的onChange
监听器:
<Tab
label='My ideas'
onChange={<Link to='/myPath'></Link>}
/>
However I'm getting following error:
但是我收到以下错误:
Uncaught Invariant Violation: Expected onChange listener to be a function, instead got type object
If I try to wrap <Tab/>
component into <Link/>
component, I'm getting error that <Tabs/>
component accepts only <Tab/>
component.
如果我尝试将<Tab/>
组件包装到<Link/>
组件中,则会收到<Tabs/>
组件仅接受<Tab/>
组件的错误消息。
This doesn't work either (no error is being produced, but clicking on Tab does not bring me to the path):
这也不起作用(没有产生错误,但单击 Tab 不会将我带到路径):
<Tab label='Most popular ideas'>
<Link to='/popular'/>
</Tab>
How do I make <Link/>
component work together with <Tabs>
and <AppBar>
? If that's not possible, I can use any other component from material-ui
library to form a proper menu.
如何使<Link/>
组件与<Tabs>
和一起工作<AppBar>
?如果这是不可能的,我可以使用material-ui
库中的任何其他组件来形成适当的菜单。
回答by hazardous
For Material UI 1.0 with Typescript: see this postby @ogglas below.
对于带有 Typescript 的 Material UI 1.0:请参阅下面@ogglas 的这篇文章。
For Material-UI 1.0 with plain JS:
对于带有纯 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>
And classes.tabLink
is defined as:
并classes.tabLink
定义为:
tabLink : {
display:"flex",
alignItems:"center",
justifyContent:"center"
}
How this works?
这是如何工作的?
All the mui 1.0 components inheriting from ButtonBase
, support a component
prop, see ButtonBase. The idea is to allow you to control what the component renders as its wrapper/root element. Tab
also has this feature although at the time of writing this answer this prop is not documented explicitly, but as Tab
inherits from ButtonBase
, all its props carry over (and the documentation does cover this).
所有继承自 的 mui 1.0 组件都ButtonBase
支持一个component
prop,参见ButtonBase。这个想法是允许您控制组件呈现为它的包装器/根元素的内容。Tab
也具有此功能,尽管在撰写此答案时该道具并未明确记录,但由于Tab
继承自ButtonBase
,其所有道具都会保留(并且文档确实涵盖了这一点)。
Another feature of ButtonBase
is that all the extra props, not in use by ButtonBase
or inherited component, are spread over the specified component
. We have used this behavior to send the to
prop used by Link
by giving it to Tab
control. You can send any additional props in the same way. Note that this is documented explicitly for both ButtonBase
and Tab
.
的另一个特点ButtonBase
是所有额外的道具,不是由ButtonBase
组件使用或继承的,都分布在指定的component
. 我们已经使用此行为通过将其交给控制来发送所to
使用的道具。您可以以相同的方式发送任何其他道具。请注意,这对于和都有明确记录。Link
Tab
ButtonBase
Tab
Thanks @josh-l for asking this to be added.
感谢 @josh-l 要求添加此内容。
回答by high incompetance
here's how you can do it now:
您现在可以这样做:
<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>
回答by Sarath Ak
You can try this simple method
你可以试试这个简单的方法
<Tab label='Most popular ideas' to='/myPath' component={Link} />
回答by Ogglas
Since we are using TypeScript I could not use @hazardous solutions. This is how we implemented routing for material-ui v1.0.0-beta.16
and react-router 4.2.0
. The reason why we are splitting this.props.history.location.pathname
is because we need to access /renewals/123
for example. If we did not do this we would get the following warning and no tab would be displayed as active: Warning: Material-UI: the value provided '/renewals/123' is invalid
由于我们使用的是 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
Complete code with imports:
带导入的完整代码:
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" alt="Logo" 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))
回答by Yaroslav Zaklinsky
TypeScript implementation of the router-driven tabs.
路由器驱动选项卡的 TypeScript 实现。
For those who look for the TypeScript implementation. Easy configurable. Driven by tabs
configuration.
对于那些寻找 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>
);
}
}
回答by Michael Lyons
So my work-around for this solution has been quite reliable, though it may be more manual of a solution than what you're looking to do.
所以我对此解决方案的解决方法非常可靠,尽管它可能比您想要做的更手动的解决方案。
The strategy that I've been using is to actually not even use the Link Component. Instead, you'll utilize the Tabs onChange property as a callback that can respond to Tab clicks, and track location manually with Props on the Parent.
我一直在使用的策略是实际上甚至不使用链接组件。相反,您将利用 Tabs onChange 属性作为可以响应 Tab 单击的回调,并使用 Parent 上的 Props 手动跟踪位置。
You can import a utility called History from react-router that will allow you to manually push locations. While using React-Router, your component tree will have access to Location prop that has a pathname key with the string of your current location.
您可以从 react-router 导入一个名为 History 的实用程序,它允许您手动推送位置。在使用 React-Router 时,您的组件树将可以访问 Location 属性,该属性具有带有当前位置字符串的路径名键。
We will manually parse this string into the components that make up your current URL, then use a Switch statement to decide both which tab is currently selected and also where to link to when a tab is clicked. (This gives you a fair amount of control over navigation)
我们将手动将此字符串解析为构成当前 URL 的组件,然后使用 Switch 语句来决定当前选择哪个选项卡以及单击选项卡时链接到的位置。(这使您可以对导航进行相当多的控制)
( e.g. ['', 'latest'] )
(例如 ['', 'latest'] )
Here is a mock up of what your component MAY look like after integrating this solution.
这是集成此解决方案后您的组件可能是什么样子的模型。
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>
);
}
});
回答by Kishan B
This is solved using the <Link />
from material-ui instead of directly using the <Link />
or <NavLink />
from react-router. The example for the same can be found in the documentation here.
这是使用<Link />
from material-ui 而不是直接使用<Link />
or <NavLink />
from react-router 来解决的。可以在此处的文档中找到相同的示例。
https://material-ui.com/components/links/
https://material-ui.com/components/links/
Also <Button />
tag has a component prop to achieve this
还<Button />
标记有分量的道具来实现这一目标
<Button color="inherit" component={Link} to={"/logout"}>Logout</Button>
An extensive discussion on this can be found here
可以在此处找到对此的广泛讨论
回答by Elia Ahadi
Here's another implementation of React with hooks, Material-UI with tabs, React Router with Link, and TypeScript.
这是带有钩子的 React 的另一个实现、带有选项卡的 Material-UI、带有链接的 React Router 和 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>
)
}
回答by jorge santos
Check this link, I implemented the solution and worked for me
检查此链接,我实施了解决方案并为我工作