Material-ui: Convertir ListItem compatible avec react-router

Créé le 30 août 2017  ·  21Commentaires  ·  Source: mui-org/material-ui

Je veux transformer chaque ListItem d'un composant List en un composant Link .

J'ai un composant Drawer composé d'un composant List ,
Cette liste est composée de 4 ListItem s.
Ce que je veux, c'est convertir chaque ListItem en un routeur React Link .
J'ai essayé de modifier la méthode onClick du tiroir, d'obtenir l'ID du ListItem cliqué, puis de changer l'emplacement de la fenêtre par programme en fonction de l'ID.
Mais je pense que cette approche est plus simple que les concepts de réaction.
J'ai aussi essayé d'ajouter Link à component prop dans ListItem , mais cela a abouti à une erreur (j'ai vu que vous avez utilisé un prop dans component dans docs).
Quelle est la manière recommandée pour répondre à cette exigence?

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
  • Réagir: ^ 15.6.1
  • Navigateur: Chrome: 60
  • react-router-dom: ^ 4.1.2
question

Commentaire le plus utile

Ceci est couvert dans la documentation: 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>
  );
}

Tous les 21 commentaires

J'ai compris, je peux envelopper ListItem dans un Link comme suit:

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

Avec cette approche, la méthode onClick sera également déclenchée.
Des suggestions ou des améliorations?

J'ai compris, je peux envelopper ListItem dans un lien comme suit

Cette solution n'est pas sémantiquement valide. Jetez un œil à la façon dont nous traitons le problème dans la documentation:

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

Vous pouvez également essayer celui-ci

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

Ceci est couvert dans la documentation: 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>
  );
}

Je ne le vois pas dans la documentation:
https://material-ui-next.com/demos/lists/
https://material-ui-next.com/demos/drawers/

C'est une chose courante lors de l'utilisation de React Router, mais on ne sait pas comment utiliser NavLink dans un ListItem .
Un Button avec component prop?
Et les accessoires de bouton et les accessoires de composant sont mélangés ensemble sur la même balise JSX?
Quel est ce genre de mixin?
Pourquoi si complexe?

Lorsque vous utilisez la solution de component={Link} comme @oliviertassinari le suggère, le style est foiré:
(Dans l'image, la souris survole "Build" mais le deuxième élément est souligné ...)

screen shot 2017-11-16 at 12 09 05

@ilyador Ce problème de style de lien d'interface utilisateur a été résolu.

@ zhangwei900808 Non, c'est toujours faux sémantiquement, cela génère <ul><a ..>...
https://codesandbox.io/s/lpwq74p30m

Utilisez simplement la réponse fournie ci-dessus

@oliviertassinari Y aurait-il un moyen plus simple de faire:

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

Pour éviter un double effet de survol, sur les MenuItem et les Button (qui ont encore une légère gouttière / rembourrage, autre problème)
Parce que c'est encombrant

démo: https://codesandbox.io/s/nk834yk5pl (sans utiliser component={Link} to="/..." mais c'est pareil)

@caub Pourquoi introduisez-vous un composant Button supplémentaire? Vous pouvez utiliser la propriété de composant sur le MenuItem.

@oliviertassinari car ul > a n'est pas valide html5 https://codesandbox.io/s/lpwq74p30m

@caub nav > a est valide.

Ok, merci, très bien https://codesandbox.io/s/lpwq74p30m

Voici la solution qui a résolu mon problème

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

Utilisez donc simplement ListItem comme
clé = {index}
** {... {vers: menu.link}} **
composant = {Link}
bouton = {true}
>

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

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

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

@mehrdaad @oliviertassinari Ça a marché ^ _ ^

C'était parfait pour moi. Merci!

La réponse la plus votée a échoué après la migration de MUI v4 (je suis sur 4.3.1) en raison de "Les composants de fonction ne peuvent pas recevoir de références. Les tentatives d'accès à cette référence échoueront. Voulez-vous utiliser React.forwardRef ()?

J'ai remplacé le composant NavLink par un wrapper comme ceci:

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

Usage:

<ListItem
    button
    component={NavLinkMui}
    to='/'
>
    <ListItemIcon>
        <SlideshowIcon />
    </ListItemIcon>
</ListItem>

@HugoGresse Oui, vous devez utiliser l'API forward ref. Vous devriez pouvoir trouver des exemples dans la documentation. J'ai mis à jour https://github.com/mui-org/material-ui/issues/7956#issuecomment -326908167 avec une réponse à jour. Faites-nous savoir si tout va bien.

@oliviertassinari c'est ce qu'il a fait.

J'ai toujours un problème avec la solution forwardRef ou le composant un où la couleur du texte du bouton ne prend pas en compte la couleur de la palette de thème. La couleur du texte doit être blanche pour correspondre à palette.type: 'dark' , mais elle est blanche.

Probablement lié à un changement sur MUI car sans les accessoires component j'ai toujours le problème.
Capture d'écran 2019-08-06 16 54 51

Pour ceux qui lisent ici, vous pouvez passer des accessoires supplémentaires au ListItemText ...

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

BTW, votre exemple est maintenant dans TS.

Voici la solution qui a résolu mon problème

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

Utilisez donc simplement ListItem comme
clé = {index}
** {... {vers: menu.link}} **
composant = {Link}
bouton = {true}
>

button={true} rend simplement le comportement de l'interface utilisateur des tiroirs identique à l'absence de lien.

Pour les autres essayant de faire cela dans Typescript:

Un exemple simple utilisant la propriété du composant ListItem pour spécifier unélément comme implémentation ListItem. L'astuce dans Typescript est d'utiliser l'opérateur de propagation (...) sur les accessoires de ListItem pour se faufiler au-delà des erreurs Typescript indésirables et inutiles que la spécification de ce type de composant et des propriétés "to" pour ListItem entraînerait normalement (Typescript erreur TS2769 La propriété 'composant' n'existe pas sur le type 'IntrinsicAttributes ...)

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

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

ou bien si vous souhaitez simplement utiliser une fonction onClick sur le ListItem par exemple:


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

''

Cela a fonctionné pour moi

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

La réponse la plus votée a échoué après la migration de MUI v4 (je suis sur 4.3.1) en raison de "Les composants de fonction ne peuvent pas recevoir de références. Les tentatives d'accès à cette référence échoueront. Voulez-vous utiliser React.forwardRef ()?

J'ai remplacé le composant NavLink par un wrapper comme ceci:

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

Usage:

<ListItem
    button
    component={NavLinkMui}
    to='/'
>
    <ListItemIcon>
        <SlideshowIcon />
    </ListItemIcon>
</ListItem>

Merci @HugoGresse Cela fonctionne parfaitement bien :-)

Cette page vous a été utile?
0 / 5 - 0 notes