Material-ui: Converter ListItem compatível com react-router

Criado em 30 ago. 2017  ·  21Comentários  ·  Fonte: mui-org/material-ui

Eu quero transformar cada ListItem em um componente List em um Link .

Eu tenho um componente Drawer composto por um componente List ,
Essa lista é composta por 4 ListItem s.
O que eu quero alcançar é converter cada ListItem em um roteador React Link .
Tentei modificar o método onClick da gaveta, obter a Id do ListItem clicado e, em seguida, alterar programaticamente a localização da janela com base na id.
Mas eu acho que essa abordagem é mais jqueryish do que os conceitos de reação.
Eu também tentei adicionar Link a component prop em ListItem , mas resultou um erro. (Eu vi que você usou um em component prop em docs).
Qual é a maneira recomendada de atender a esse 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
  • Reação: ^ 15.6.1
  • Navegador: Chrome: 60
  • react-router-dom: ^ 4.1.2
question

Comentários muito úteis

Isso é abordado na documentação: 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 comentários

Eu descobri, posso envolver ListItem em um Link como segue:

<Link to="users" style={{ textDecoration: 'none' }}>
      <ListItem button >
        <ListItemIcon>
          <Person />
        </ListItemIcon>
        <ListItemText primary={<FormattedMessage id="user" />} />
      </ListItem>
    </Link>

Com essa abordagem, o método onClick também será disparado.
Alguma sugestão ou importação?

Eu descobri, posso envolver ListItem em um Link da seguinte maneira

Esta solução não é semanticamente válida. Dê uma olhada em como lidamos com o problema na documentação:

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

Você também pode tentar este

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

Isso é abordado na documentação: 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>
  );
}

Não vejo isso na documentação:
https://material-ui-next.com/demos/lists/
https://material-ui-next.com/demos/drawers/

Isso é comum ao usar o Roteador React, mas não está claro como usar NavLink em um ListItem .
Um Button com component prop?
E os adereços de botão e os adereços de componentes são misturados na mesma tag JSX?
O que é esse tipo de mixin?
Por que tão complexo?

Ao usar a solução de @mehrdaad , os links parecem uma lista MUI normal, mas ao usar component={Link} como @oliviertassinari sugeriu, o estilo fica confuso:
(Na imagem, o mouse está pairando sobre "Construir", mas o segundo item fica sublinhado ...)

screen shot 2017-11-16 at 12 09 05

@ilyador Esse problema de estilo de link da IU foi resolvido.

@ zhangwei900808 Não, isso ainda está errado semanticamente, ele gera <ul><a ..>...
https://codesandbox.io/s/lpwq74p30m

Basta usar a resposta fornecida acima

@oliviertassinari Haveria uma maneira mais fácil de fazer:

const styles = {
    li: {
        '&:hover': {
            backgroundColor: 'transparent',
        },
    },
};
// ...
<MenuItem disableGutters className={classes.li}>
    <Button component={Link} to="/logout" ...

Para evitar um efeito de foco duplo, em MenuItem e em Button (que ainda têm uma pequena calha / preenchimento, outro problema)
Porque isso é complicado

demonstração: https://codesandbox.io/s/nk834yk5pl (não usando component={Link} to="/..." mas é o mesmo)

@caub Por que você está introduzindo um componente extra de botão? Você pode usar a propriedade do componente no MenuItem.

@oliviertassinari porque ul > a não é html5 válido https://codesandbox.io/s/lpwq74p30m

@caub nav > a é válido.

Aqui está a solução que resolveu meu 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>

Portanto, basta usar ListItem como
chave = {índice}
** {... {to: menu.link}} **
componente = {Link}
botão = {true}
>

import {Link} from 'react-router-dom'

<ListItem component={Link} to="/" button>

// or
<ListItem component={props => <Link to="/" {...props} />} button>

@mehrdaad @oliviertassinari Funcionou ^ _ ^

Isso foi perfeito para mim. Obrigado!

A resposta mais votada falhou após a migração do MUI v4 (estou em 4.3.1) devido a "Os componentes da função não podem receber referências. As tentativas de acessar esta referência falharão. Você quis dizer usar React.forwardRef ()?"

Substituí o componente NavLink por um invólucro assim:

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 Sim, você precisa usar a API forward ref. Você deve encontrar exemplos na documentação. Atualizei https://github.com/mui-org/material-ui/issues/7956#issuecomment -326908167 com uma resposta atualizada. Deixe-nos saber se está tudo bem.

@oliviertassinari sim.

Ainda estou tendo problemas com a solução forwardRef ou com o componente um em que a cor do texto do botão não leva em consideração a cor da paleta do tema. A cor do texto deve ser branca para corresponder a palette.type: 'dark' , mas é branca.

Provavelmente vinculado a uma mudança no MUI porque sem os component props ainda estou tendo o problema.
Capture d'écran 2019-08-06 16 54 51

Para aqueles que estão lendo aqui, você pode passar adereços adicionais para ListItemText ...

<ListItemText
    primaryTypographyProps={{
        color: 'textPrimary'
    }}
    primary="Dashboard"
/>

BTW, seu exemplo agora está em TS.

Aqui está a solução que resolveu meu 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>

Portanto, basta usar ListItem como
chave = {índice}
** {... {to: menu.link}} **
componente = {Link}
botão = {true}
>

button={true} simplesmente torna o comportamento da UI das gavetas o mesmo que nenhum link.

Para outras pessoas que tentam fazer isso no texto datilografado:

Um exemplo simples usando a propriedade do componente ListItem para especificar umelemento como a implementação ListItem. O truque em Typescript é usar o operador de propagação (...) nos adereços do ListItem para passar furtivamente pelos erros de Typescript indesejados e desnecessários que a especificação desse tipo de componente e as propriedades "para" do ListItem normalmente causariam (Typescript erro TS2769 Propriedade 'componente' não existe no tipo 'IntrinsicAttributes ...)

import { NavLink } from "react-router-dom";

        <List>
            <ListItem button={true} {...{ component: NavLink, to: "/Somewhere" }}>
                <ListItemIcon>
                    <ShoppingCartIcon />
                </ListItemIcon>
                <ListItemText primary="Orders" />
            </ListItem>
        </List>

ou alternativamente, se você quiser apenas usar uma função onClick no ListItem, por exemplo:


    <List>
        <ListItem button={true} {...{ onClick: () => alert("foo") }}>
            <ListItemIcon>
                <DashboardIcon />
            </ListItemIcon>
            <ListItemText primary="Dashboard" />
        </ListItem>
    </List>

`` `

Isso funcionou para mim

const NavLinkMui = React.forwardRef((props, ref) => (
  <NavLink {...props} activeClassName="Mui-selected" ref={ref} />
))
<ListItem button component={NavLinkMui} to={to}>{text}</ListItem>

A resposta mais votada falhou após a migração do MUI v4 (estou em 4.3.1) devido a "Os componentes da função não podem receber referências. As tentativas de acessar esta referência falharão. Você quis dizer usar React.forwardRef ()?"

Substituí o componente NavLink por um invólucro assim:

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>

Obrigado @HugoGresse Isso funciona perfeitamente bem :-)

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

reflog picture reflog  ·  3Comentários

anthony-dandrea picture anthony-dandrea  ·  3Comentários

pola88 picture pola88  ·  3Comentários

finaiized picture finaiized  ·  3Comentários

newoga picture newoga  ·  3Comentários