javascript - example - Material-ui agregando componente de enlace desde react-router
react list (7)
Implementación de TypeScript de las pestañas controladas por el enrutador.
Para aquellos que buscan la implementación de TypeScript. Fácil configurable. Impulsado por la configuración de 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>
);
}
}
Estoy luchando para agregar el componente <Link/>
a mi barra de aplicaciones de ui de material
Esta es mi clase de navegación:
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>
)
}
}
Las pestañas son cliqueables, tienen animaciones fluidas, eso es genial. Pero, ¿cómo los conecto con react-router
y su componente '' <Link/>
?
He intentado agregar el oyente onChange
esa manera:
<Tab
label=''My ideas''
onChange={<Link to=''/myPath''></Link>}
/>
Sin embargo estoy recibiendo el siguiente error:
Uncaught Invariant Violation: Expected onChange listener to be a function, instead got type object
Si trato de envolver el componente <Tab/>
en el componente <Link/>
, obtengo el error de que el componente <Tabs/>
solo acepta el componente <Tab/>
.
Esto tampoco funciona (no se produce ningún error, pero hacer clic en Tab no me lleva a la ruta):
<Tab label=''Most popular ideas''>
<Link to=''/popular''/>
</Tab>
¿Cómo hago para que el componente <Link/>
funcione junto con <Tabs>
y <AppBar>
? Si eso no es posible, puedo usar cualquier otro componente de la biblioteca material-ui
para formar un menú adecuado.
Así es como puedes hacerlo ahora:
<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>
Otra forma fácil es proporcionar apoyo al elemento Tab.
<Tab label="My label" route="/go-here" onActive={handleActive} />
Entonces manejarActive tiene este aspecto
handleActive(tab) {
/*accessing your route from*/ tab.props.route
}
De esta manera, se documenta en la página de componentes como un tercer ejemplo de la función Activa.
Para Material UI 1.0 con Typescript: vea este post por @ogglas a continuación.
Para Material-UI 1.0 con JS liso:
<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>
Y classes.tabLink
se define como:
tabLink : {
display:"flex",
alignItems:"center",
justifyContent:"center"
}
¿Cómo funciona esto?
Todos los componentes mui 1.0 que se heredan de ButtonBase
, son compatibles con un component
prop, consulte ButtonBase . La idea es permitirle controlar lo que el componente representa como su elemento contenedor / raíz. Tab
también tiene esta característica, aunque en el momento de escribir esta respuesta, esta propuesta no está documentada explícitamente , pero como Tab
hereda de ButtonBase
, todas sus ButtonBase
(y la documentación cubre esto).
Otra característica de ButtonBase
es que todos los accesorios adicionales, que no están siendo utilizados por ButtonBase
o componente heredado, están repartidos en el component
especificado. Hemos utilizado este comportamiento para enviar el to
prop utilizado por Link
dándolo al control Tab
. Puedes enviar cualquier accesorio adicional de la misma manera. Tenga en cuenta que esto está documentado explícitamente para ButtonBase
y Tab
.
Gracias @ josh-l por pedir que se agregue esto.
Por lo tanto, mi solución alternativa para esta solución ha sido bastante confiable, aunque puede ser una solución más manual que lo que está buscando hacer.
La estrategia que he estado usando es en realidad ni siquiera usar el componente de enlace. En su lugar, utilizará la propiedad Tabs onChange como una devolución de llamada que puede responder a los clics en las pestañas, y rastrear la ubicación manualmente con Props en el Padre.
Puede importar una utilidad llamada Historial desde el enrutador de reacción que le permitirá ingresar ubicaciones manualmente. Mientras usa React-Router, su árbol de componentes tendrá acceso a la propiedad de ubicación que tiene una clave de ruta con la cadena de su ubicación actual.
Analizaremos manualmente esta cadena en los componentes que conforman su URL actual, luego usaremos una declaración Switch para decidir qué pestaña está seleccionada actualmente y también a dónde vincular cuando se hace clic en una pestaña. (Esto le da una buena cantidad de control sobre la navegación)
(por ejemplo, ['''', ''último''])
A continuación se presenta una maqueta de cómo puede verse su componente después de integrar esta solución.
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>
);
}
});
Puedes probar este sencillo método
<Tab label=''Most popular ideas'' to=''/myPath'' component={Link} />
Ya que estamos usando TypeScript, no pude usar las soluciones @hazardous. Así es como implementamos el enrutamiento para material-ui v1.0.0-beta.16
y react-router 4.2.0
. La razón por la que estamos dividiendo this.props.history.location.pathname
es porque necesitamos acceder a /renewals/123
por ejemplo. Si no hiciéramos esto, recibiríamos la siguiente advertencia y ninguna pestaña se mostraría como activa: Warning: Material-UI: the value provided ''/renewals/123'' is invalid
Código completo con importaciones:
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" />
</div>
</Grid>
</Grid>
</div>
);
}
}
export default withStyles(styles)(withRouter(Menu))