Material-ui: Convertir ListItem compatible con react-router

Creado en 30 ago. 2017  ·  21Comentarios  ·  Fuente: mui-org/material-ui

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);
  • Material-UI: 1.0.6-beta
  • Reaccionar: ^ 15.6.1
  • Navegador: Chrome: 60
  • react-router-dom: ^ 4.1.2
question

Comentario más útil

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>
  );
}

Todos 21 comentarios

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:

https://github.com/callemall/material-ui/blob/92bd073015b9cc0dff3a26195fcb49153ddaab78/docs/src/modules/components/AppDrawerNavItem.js#L71 -L83

También puedes probar este

https://github.com/callemall/material-ui/blob/92bd073015b9cc0dff3a26195fcb49153ddaab78/docs/src/pages/demos/buttons/FlatButtons.js#L40 -L42

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 ...)

screen shot 2017-11-16 at 12 09 05

@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.

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}
>

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.
Capture d'écran 2019-08-06 16 54 51

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 unelemento como la implementación ListItem. El truco en TypeScript es usar el operador de extensión (...) en los accesorios de ListItem para escabullirse de los errores de Typecript no deseados e innecesarios que normalmente se producirían al especificar este tipo de componente y las propiedades "to" para ListItem (Typecript error TS2769 La propiedad 'componente' no existe en el tipo 'IntrinsicAttributes ...)

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 :-)

¿Fue útil esta página
0 / 5 - 0 calificaciones