Material-ui: Konvertieren Sie ListItem kompatibel mit React-Router

Erstellt am 30. Aug. 2017  ·  21Kommentare  ·  Quelle: mui-org/material-ui

Ich möchte jedes ListItem in einer List zu einer Link Komponente machen.

Ich habe eine Drawer -Komponente, die aus einer List -Komponente besteht.
Diese Liste besteht aus 4 ListItem s.
Was ich erreichen möchte, ist, dass jedes ListItem in einen React-Router Link konvertiert wird.
Ich habe versucht, die onClick-Methode der Schublade zu ändern, die ID des angeklickten ListItem abzurufen und dann die Position des Fensters basierend auf der ID programmgesteuert zu ändern.
Aber ich denke, dieser Ansatz ist fragwürdiger als die Reaktionskonzepte.
Ich habe auch versucht, Link zu component prop in ListItem hinzuzufügen, aber es kam zu einem Fehler. (Ich habe gesehen, dass Sie in component prop in verwendet haben docs).
Was ist der empfohlene Weg, um diese Anforderung zu erreichen?

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
  • Reaktion: ^ 15.6.1
  • Browser: Chrome: 60
  • React-Router-Dom: ^ 4.1.2
question

Hilfreichster Kommentar

Dies wird in der Dokumentation behandelt: 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>
  );
}

Alle 21 Kommentare

Ich habe herausgefunden, dass ich ListItem Link wie folgt in

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

Bei diesem Ansatz wird auch die onClick-Methode ausgelöst.
Anregungen oder Verbesserungen?

Ich habe herausgefunden, dass ich ListItem wie folgt in einen Link einbinden kann

Diese Lösung ist semantisch nicht gültig. Schauen Sie sich an, wie wir mit dem Problem in der Dokumentation umgehen:

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

Sie können diesen auch ausprobieren

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

Dies wird in der Dokumentation behandelt: 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>
  );
}

Ich sehe es nicht in den Dokumenten:
https://material-ui-next.com/demos/lists/
https://material-ui-next.com/demos/drawers/

Dies ist eine häufige Sache bei der Verwendung von React Router, aber es ist nicht klar, wie NavLink in einem ListItem .
Ein Button mit component Prop?
Und die Button-Requisiten und die Komponenten-Requisiten werden auf demselben JSX-Tag zusammengemischt?
Was ist das für ein Mixin?
Warum so komplex?

Wenn Sie die Lösung von component={Link} wie von @oliviertassinari vorgeschlagen, wird der Stil durcheinander gebracht:
(Im Bild schwebt die Maus über "Build", aber das zweite Element wird unterstrichen ...)

screen shot 2017-11-16 at 12 09 05

@ilyador Dieses Problem mit dem UI-

@ zhangwei900808 Nein, das ist semantisch immer noch falsch, es generiert <ul><a ..>...
https://codesandbox.io/s/lpwq74p30m

Verwenden Sie einfach die oben angegebene Antwort

@oliviertassinari Gibt es einen einfacheren Weg:

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

Um einen Double-Hover-Effekt zu vermeiden, auf dem MenuItem und auf Button (die immer noch eine leichte Rinne / Polsterung haben, ein weiteres Problem)
Weil das umständlich ist

Demo: https://codesandbox.io/s/nk834yk5pl (ohne component={Link} to="/..." aber das ist das gleiche)

@caub Warum führen Sie eine zusätzliche Button-Komponente ein? Sie können die Komponenteneigenschaft im MenuItem verwenden.

@oliviertassinari, weil ul > a nicht gültig ist html5 https://codesandbox.io/s/lpwq74p30m

@caub nav > a ist gültig.

Hier ist die Lösung, die mein Problem gelöst hat

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

Verwenden Sie daher einfach ListItem als
key = {index}
** {... {to: menu.link}} **
component = {Link}
button = {true}
>

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

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

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

@mehrdaad @oliviertassinari Es hat funktioniert ^ _ ^

Das war perfekt für mich. Vielen Dank!

Die am häufigsten gewählte Antwort schlug nach der Migration von MUI v4 (ich bin auf 4.3.1) aufgrund von "Funktionskomponenten können keine Refs zugewiesen werden. Versuche, auf diese Ref zuzugreifen, schlagen fehl. Wollten Sie React.forwardRef () verwenden?"

Ich habe die NavLink -Komponente durch einen Wrapper wie folgt ersetzt:

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

Verwendung:

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

@HugoGresse Ja, Sie müssen die Forward-Ref-API verwenden. Beispiele sollten Sie in der Dokumentation finden können. Ich habe https://github.com/mui-org/material-ui/issues/7956#issuecomment -326908167 mit einer aktuellen Antwort aktualisiert. Lassen Sie uns wissen, ob es in Ordnung ist.

@oliviertassinari hat es getan.

Ich habe immer noch ein Problem mit der forwardRef-Lösung oder der Komponente 1, bei der die Textfarbe der Schaltfläche die Farbe der Themenpalette nicht berücksichtigt. Die Textfarbe sollte weiß sein, um mit palette.type: 'dark' , ist jedoch weiß.

Wahrscheinlich im Zusammenhang mit einer Änderung der MUI, da ich ohne die component Requisiten immer noch das Problem habe.
Capture d'écran 2019-08-06 16 54 51

Für diejenigen, die hier nachlesen, können Sie zusätzliche Requisiten an ListItemText übergeben ...

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

Übrigens, Ihr Beispiel ist jetzt in TS.

Hier ist die Lösung, die mein Problem gelöst hat

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

Verwenden Sie daher einfach ListItem als
key = {index}
** {... {to: menu.link}} **
component = {Link}
button = {true}
>

button={true} macht das UI-Verhalten von Schubladen einfach so wie kein Link.

Für andere, die dies in Typescript versuchen:

Ein einfaches Beispiel für die Verwendung der ListItem-Komponenteneigenschaft zur Angabe von aElement als ListItem-Implementierung. Der Trick in Typescript besteht darin, den Spread-Operator (...) auf den Requisiten des ListItem zu verwenden, um die unerwünschten und nicht benötigten Typescript-Fehler zu überwinden, die die Angabe dieses Komponententyps und der "to" -Eigenschaften für das ListItem normalerweise verursachen würde (Typescript) Fehler TS2769 Eigenschaft 'Komponente' existiert für Typ 'IntrinsicAttributes ...) nicht

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

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

oder alternativ, wenn Sie nur eine onClick-Funktion für das ListItem verwenden möchten, zum Beispiel:


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

`` `

Das hat bei mir funktioniert

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

Die am häufigsten gewählte Antwort schlug nach der Migration von MUI v4 (ich bin auf 4.3.1) aufgrund von "Funktionskomponenten können keine Refs zugewiesen werden. Versuche, auf diese Ref zuzugreifen, schlagen fehl. Wollten Sie React.forwardRef () verwenden?"

Ich habe die NavLink -Komponente durch einen Wrapper wie folgt ersetzt:

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

Verwendung:

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

Danke @HugoGresse Das funktioniert einwandfrei :-)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen