Material-ui добавляет компонент Link из реактивного маршрутизатора
Я изо всех сил пытаюсь добавить компонент <Link/>
в панель приложения-материалов
Это мой класс навигации:
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>
)
}
}
Что выглядит хорошо: ![Navbar]()
Вкладки кликабельны, имеют плавную анимацию, это круто. Но как мне соединить их с 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
Для Material UI 1.0 с Typescript: см. Этот пост @ogglas ниже.
Для Material-UI 1.0 с простым JS:
<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"
}
Как это работает?
Все компоненты mui 1.0, наследуемые от ButtonBase
, поддерживают опору component
, см. ButtonBase. Идея состоит в том, чтобы позволить вам контролировать то, что компонент отображает как его элемент оболочки/корня. Tab
также есть эта функция, хотя на момент написания этого ответа эта опора явно не документирована, но, поскольку Tab
наследует от ButtonBase
, все ее реквизиты переносятся (и документация действительно охватывает это).
Еще одной особенностью ButtonBase
является то, что все дополнительные реквизиты, не используемые ButtonBase
или унаследованным компонентом, распространяются на указанный component
. Мы использовали это поведение, чтобы отправить to
проп используется Link
, давая его Tab
управления. Вы можете отправить любые дополнительные реквизиты таким же образом. Обратите внимание, что это задокументировано явно как для ButtonBase
и для Tab
.
Спасибо @josh-l за то, что попросили добавить это.
Ответ 2
вот как вы можете это сделать сейчас:
<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>
Ответ 3
Поскольку мы используем TypeScript, я не мог использовать решения @hazardous. Так мы реализовали маршрутизацию для material-ui v1.0.0-beta.16
и 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" 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))
Ответ 4
Вы можете попробовать этот простой метод
<Tab label='Most popular ideas' to='/myPath' component={Link} />
Ответ 5
Итак, моя работа для этого решения была достаточно надежной, хотя она может быть более ручной для решения, чем то, что вы хотите сделать.
Стратегия, которую я использовал, состоит в том, чтобы на самом деле даже не использовать компонент Link. Вместо этого вы будете использовать свойство Tabs onChange в качестве обратного вызова, которое может реагировать на нажатия Tab и отслеживать местоположение вручную с помощью реквизитов на родительском устройстве.
Вы можете импортировать утилиту "История" из "реактивного маршрутизатора", которая позволит вам вручную удалять местоположения. При использовании React-Router ваше дерево компонентов будет иметь доступ к Location prop, у которого есть ключ пути с строкой вашего текущего местоположения.
Мы вручную проанализируем эту строку в компонентах, составляющих ваш текущий 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>
);
}
});
Ответ 6
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>
);
}
}
Ответ 7
Это решается с помощью <Link />
из material-ui вместо непосредственного использования <Link />
или <NavLink />
из реактивного маршрутизатора. Пример того же можно найти в документации здесь.
https://material-ui.com/components/links/
Кроме того, тег <Button />
имеет пропеллер для достижения этой цели
<Button color="inherit" component={Link} to={"/logout"}>Logout</Button>
Обширное обсуждение этого можно найти здесь
https://github.com/mui-org/material-ui/issues/850
Ответ 8
Проверьте эту ссылку, я реализовал решение и работал для меня
Композиция в материале UI
Ответ 9
Другим простым способом является поддержка элемента Tab.
<Tab label="My label" route="/go-here" onActive={handleActive} />
Тогда handleActive выглядит как
handleActive(tab) {
/*accessing your route from*/ tab.props.route
}
этот способ документирован на странице компонентов в качестве третьего примера функцииActive