Quiero convertir cada ListItem
en un List
en un componente Link
.
Tengo un componente Drawer
compuesto por un componente List
,
Esa Lista se compone de 4 ListItem
s.
Lo que quiero lograr es convertir cada ListItem
en un enrutador React Link
.
Intenté modificar el método onClick del cajón, obtener el Id del ListItem en el que se hizo clic y luego cambiar mediante programación la ubicación de la ventana en función del id.
Pero creo que este enfoque es más curioso que los conceptos de reacción.
También intenté agregar Link
a component
prop en ListItem
, pero resultó en un error. (He visto que ha usado un en component
prop en docs).
¿Cuál es la forma recomendada de lograr este requisito?
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Drawer from 'material-ui/Drawer';
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Face from 'material-ui-icons/Face';
import Person from 'material-ui-icons/Person';
import Assignment from 'material-ui-icons/Assignment';
import Link from 'react-router-dom/Link';
import { FormattedMessage } from 'react-intl';
const styles = {
list: {
width: 250,
flex: 'initial',
},
};
class UndockedDrawer extends Component {
constructor() {
super();
this.onClick = this.onClick.bind(this);
}
onClick(e, data) {
console.log(data);
this.props.onRequestClose();
}
render() {
const classes = this.props.classes;
const sidebarListItems = (
<div>
<ListItem button >
<ListItemIcon>
<Person />
</ListItemIcon>
<ListItemText primary={<FormattedMessage id="user" />} />
</ListItem>
<ListItem button>
<ListItemIcon>
<Assignment />
</ListItemIcon>
<ListItemText primary={<FormattedMessage id="consultant" />} />
</ListItem>
<ListItem button>
<ListItemIcon>
<Face />
</ListItemIcon>
<ListItemText primary={<FormattedMessage id="student" />} />
</ListItem>
</div>
);
const sidebarList = (
<div>
<List className={classes.list} >
{sidebarListItems}
</List>
</div>
);
return (
<div>
<Drawer
anchor="right"
open={this.props.open}
onRequestClose={this.props.onRequestClose}
onClick={this.onClick}
>
{sidebarList}
</Drawer>
</div>
);
}
}
UndockedDrawer.propTypes = {
classes: PropTypes.shape({}).isRequired,
open: PropTypes.bool.isRequired,
onRequestClose: PropTypes.func.isRequired,
};
export default withStyles(styles)(UndockedDrawer);
Me di cuenta de que puedo envolver ListItem
en un Link
siguiente manera:
<Link to="users" style={{ textDecoration: 'none' }}>
<ListItem button >
<ListItemIcon>
<Person />
</ListItemIcon>
<ListItemText primary={<FormattedMessage id="user" />} />
</ListItem>
</Link>
Con este enfoque, el método onClick también se activará.
¿Alguna sugerencia o mejora?
Me di cuenta de que puedo envolver ListItem en un enlace de la siguiente manera
Esta solución no es semánticamente válida. Eche un vistazo a cómo manejamos el problema en la documentación:
También puedes probar este
Esto se cubre en la documentación: https://material-ui.com/components/buttons/#third -party-routing-library.
import React from 'react';
import { MemoryRouter as Router } from 'react-router';
import { Link, LinkProps } from 'react-router-dom';
import ListItem from '@material-ui/core/ListItem';
import { Omit } from '@material-ui/types';
// The usage of React.forwardRef will no longer be required for react-router-dom v6.
// see https://github.com/ReactTraining/react-router/issues/6056
const AdapterLink = React.forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => (
<Link innerRef={ref as any} {...props} />
));
const CollisionLink = React.forwardRef<HTMLAnchorElement, Omit<LinkProps, 'innerRef' | 'to'>>(
(props, ref) => <Link innerRef={ref as any} to="/getting-started/installation/" {...props} />,
);
export default function ButtonRouter() {
return (
<Router>
<ListItem color="primary" component={AdapterLink} to="/">
Simple case
</ListItem>
<ListItem component={CollisionLink}>Avoids props collision</ListItem>
</Router>
);
}
No lo veo en los documentos:
https://material-ui-next.com/demos/lists/
https://material-ui-next.com/demos/drawers/
Esto es algo común cuando se usa React Router, pero no está claro cómo usar NavLink
en un ListItem
.
A Button
con component
prop?
¿Y los accesorios del botón y los accesorios del componente se mezclan en la misma etiqueta JSX?
¿Qué es este tipo de mixin?
¿Por qué tan complejo?
Cuando se usa la solución de @mehrdaad , los enlaces se ven como una lista MUI normal, pero cuando se usa component={Link}
como sugirió @oliviertassinari , el estilo se estropea:
(En la imagen, el mouse se desplaza sobre "Crear", pero el segundo elemento aparece subrayado ...)
@ilyador Este problema de estilo de enlace de la interfaz de usuario ha sido resuelto.
@ zhangwei900808 No, todavía está mal semánticamente, genera <ul><a ..>...
https://codesandbox.io/s/lpwq74p30m
Simplemente use la respuesta proporcionada anteriormente
@oliviertassinari ¿Habría una manera más fácil de hacer:
const styles = {
li: {
'&:hover': {
backgroundColor: 'transparent',
},
},
};
// ...
<MenuItem disableGutters className={classes.li}>
<Button component={Link} to="/logout" ...
Para evitar un doble efecto de desplazamiento, en MenuItem
y en Button
(que todavía tienen un ligero margen / relleno, otro problema)
Porque esto es engorroso
demostración: https://codesandbox.io/s/nk834yk5pl (sin usar component={Link} to="/..."
pero es lo mismo)
@caub ¿
@oliviertassinari porque ul > a
no es válido html5 https://codesandbox.io/s/lpwq74p30m
@caub nav > a
es válido.
Ok, gracias, bien https://codesandbox.io/s/lpwq74p30m
Aquí está la solución que resolvió mi problema.
<List>
{menus.map((menu: any, index: any) => {
return (
<ListItem
key={index}
{...{ to: menu.link }}
component={Link}
button={true}
>
<ListItemIcon>{menu.icon}</ListItemIcon>
<ListItemText primary={menu.text} />
</ListItem>
);
})}
</List>
Por lo tanto, use ListItem como
** {... {to: menu.link}} **
componente = {Enlace}
button = {true}
>
import {Link} from 'react-router-dom' <ListItem component={Link} to="/" button> // or <ListItem component={props => <Link to="/" {...props} />} button>
@mehrdaad @oliviertassinari Ha funcionado ^ _ ^
Esto fue perfecto para mí. ¡Gracias!
La respuesta más votada falló después de la migración de MUI v4 (estoy en 4.3.1) debido a que "No se pueden proporcionar referencias a los componentes de la función. Los intentos de acceder a esta referencia fallarán. ¿Querías usar React.forwardRef ()?"
Reemplacé el componente NavLink
por un contenedor así:
import React, { Component } from "react"
import { NavLink } from "react-router-dom"
/**
* React Router Nav Link wrapper to forward the ref to fix "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?"
*
* From https://material-ui.com/guides/composition/#caveat-with-refs
*/
class NavLinkMui extends Component {
render() {
const { forwardedRef, ...props } = this.props
return <NavLink {...props} ref={forwardedRef} />
}
}
export default NavLinkMui
Uso:
<ListItem
button
component={NavLinkMui}
to='/'
>
<ListItemIcon>
<SlideshowIcon />
</ListItemIcon>
</ListItem>
@HugoGresse Sí, debe utilizar la API de referencia directa. Debería poder encontrar ejemplos en la documentación. He actualizado https://github.com/mui-org/material-ui/issues/7956#issuecomment -326908167 con una respuesta actualizada. Háganos saber si está bien.
@oliviertassinari lo hizo.
Todavía tengo algún problema con la solución forwardRef o el Componente uno, donde el color del texto del Botón no tiene en cuenta el color de la paleta de temas. El color del texto debe ser blanco para que coincida con palette.type: 'dark'
, pero es blanco.
Probablemente vinculado a un cambio en MUI porque sin los accesorios component
todavía tengo el problema.
Para aquellos que leen aquí, pueden pasar accesorios adicionales al ListItemText ...
<ListItemText
primaryTypographyProps={{
color: 'textPrimary'
}}
primary="Dashboard"
/>
Por cierto, su ejemplo está ahora en TS.
Aquí está la solución que resolvió mi problema.
<List> {menus.map((menu: any, index: any) => { return ( <ListItem key={index} {...{ to: menu.link }} component={Link} button={true} > <ListItemIcon>{menu.icon}</ListItemIcon> <ListItemText primary={menu.text} /> </ListItem> ); })} </List>
Por lo tanto, use ListItem como
clave = {índice}
** {... {to: menu.link}} **
componente = {Enlace}
button = {true}
>
button={true}
simplemente hace que el comportamiento de la interfaz de usuario de los cajones sea el mismo que sin vínculo.
Para otros que intentan hacer esto en Typecript:
Un ejemplo simple que usa la propiedad del componente ListItem para especificar un
import { NavLink } from "react-router-dom";
<List>
<ListItem button={true} {...{ component: NavLink, to: "/Somewhere" }}>
<ListItemIcon>
<ShoppingCartIcon />
</ListItemIcon>
<ListItemText primary="Orders" />
</ListItem>
</List>
o alternativamente si solo desea usar una función onClick en ListItem, por ejemplo:
<List>
<ListItem button={true} {...{ onClick: () => alert("foo") }}>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
</List>
''
Esto funcionó para mi
const NavLinkMui = React.forwardRef((props, ref) => (
<NavLink {...props} activeClassName="Mui-selected" ref={ref} />
))
<ListItem button component={NavLinkMui} to={to}>{text}</ListItem>
La respuesta más votada falló después de la migración de MUI v4 (estoy en 4.3.1) debido a que "No se pueden proporcionar referencias a los componentes de la función. Los intentos de acceder a esta referencia fallarán. ¿Querías usar React.forwardRef ()?"
Reemplacé el componente
NavLink
por un contenedor así:import React, { Component } from "react" import { NavLink } from "react-router-dom" /** * React Router Nav Link wrapper to forward the ref to fix "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?" * * From https://material-ui.com/guides/composition/#caveat-with-refs */ class NavLinkMui extends Component { render() { const { forwardedRef, ...props } = this.props return <NavLink {...props} ref={forwardedRef} /> } } export default NavLinkMui
Uso:
<ListItem button component={NavLinkMui} to='/' > <ListItemIcon> <SlideshowIcon /> </ListItemIcon> </ListItem>
Gracias @HugoGresse Esto funciona perfectamente bien :-)
Comentario más útil
Esto se cubre en la documentación: https://material-ui.com/components/buttons/#third -party-routing-library.