Material-ui: Преобразование ListItem, совместимого с реактивным маршрутизатором

Созданный на 30 авг. 2017  ·  21Комментарии  ·  Источник: mui-org/material-ui

Я хочу преобразовать каждый ListItem в List в компонент Link .

У меня есть компонент Drawer состоящий из компонента List ,
Этот список состоит из 4 ListItem s.
Я хочу преобразовать каждый ListItem в маршрутизатор React Link .
Я попытался изменить метод onClick ящика, получить идентификатор выбранного элемента ListItem, а затем программно изменить расположение окна на основе идентификатора.
Но я действительно думаю, что этот подход более резкий, чем концепции реакции.
Я также пытался добавить Link к component prop в ListItem , но это привело к ошибке (я видел, что вы использовали component prop в документы).
Каков рекомендуемый способ выполнения этого требования?

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-бета
  • Реагировать: ^ 15.6.1
  • Браузер: Chrome: 60
  • реагировать-маршрутизатор-дом: ^ 4.1.2
question

Самый полезный комментарий

Это описано в документации: 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>
  );
}

Все 21 Комментарий

Я понял, я могу обернуть ListItem в Link следующим образом:

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

При таком подходе будет запущен и метод onClick.
Есть предложения или улучшения?

Я понял, я могу обернуть ListItem в ссылку следующим образом

Это решение семантически неверно. Посмотрите, как мы решаем проблему в документации:

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

Вы также можете попробовать это

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

Это описано в документации: 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>
  );
}

Я не вижу этого в документации:
https://material-ui-next.com/demos/lists/
https://material-ui-next.com/demos/drawers/

Это обычная вещь при использовании React Router, но неясно, как использовать NavLink в ListItem .
Button с опорой component ?
А свойства кнопок и компонентов смешиваются вместе в одном теге JSX?
Что это за миксин?
Почему так сложно?

При использовании решения @mehrdaad ссылки выглядят как обычный список MUI, но при использовании component={Link} предложенного @oliviertassinari , стиль
(На изображении указатель мыши находится над «Сборкой», но второй элемент подчеркивается ...)

screen shot 2017-11-16 at 12 09 05

@ilyador Эта проблема со стилем ссылки интерфейса была решена.

@ zhangwei900808 Нет, это все еще семантически неверно, генерирует <ul><a ..>...
https://codesandbox.io/s/lpwq74p30m

Просто используйте ответ, указанный выше

@oliviertassinari Есть ли более простой способ сделать:

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

Чтобы избежать эффекта двойного наведения, на MenuItem и на Button (у которых все еще есть небольшой желоб / отступ, другая проблема)
Потому что это громоздко

демо: https://codesandbox.io/s/nk834yk5pl (без использования component={Link} to="/..." но это то же самое)

@caub Почему вы вводите дополнительный компонент Button? Вы можете использовать свойство компонента в MenuItem.

@oliviertassinari, потому что ul > a недействителен html5 https://codesandbox.io/s/lpwq74p30m

@caub nav > a действительно.

Хорошо, спасибо, отлично https://codesandbox.io/s/lpwq74p30m

Вот решение, которое решило мою проблему

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

Таким образом, просто используйте ListItem как
ключ = {индекс}
** {... {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 Сработало ^ _ ^

Это было идеально для меня. Благодаря!

Ответ, получивший наибольшее количество голосов, не удался после миграции MUI v4 (я на 4.3.1) из-за того, что «Функциональным компонентам нельзя давать ссылки. Попытки доступа к этой ссылке не удастся. Вы имели в виду использовать React.forwardRef ()?»

Я заменил компонент NavLink на оболочку следующим образом:

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

Применение:

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

@HugoGresse Да, вам нужно использовать API прямой ссылки. Вы должны найти примеры в документации. Я обновил https://github.com/mui-org/material-ui/issues/7956#issuecomment -326908167 актуальным ответом. Сообщите нам, если все в порядке.

@oliviertassinari это сделал.

У меня все еще есть проблема с решением forwardRef или с первым компонентом, где цвет текста кнопки не учитывает цвет палитры темы. Цвет текста должен быть белым, чтобы соответствовать palette.type: 'dark' , но белым.

Вероятно, это связано с изменением MUI, потому что без реквизита component меня все еще есть проблема.
Capture d'écran 2019-08-06 16 54 51

Для тех, кто читает здесь, вы можете передать дополнительные свойства ListItemText ...

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

Кстати, ваш пример теперь в TS.

Вот решение, которое решило мою проблему

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

Таким образом, просто используйте ListItem как
ключ = {индекс}
** {... {to: menu.link}} **
component = {Link}
button = {true}
>

button={true} просто делает поведение ящиков пользовательским интерфейсом таким же, как отсутствие ссылки.

Для других, пытающихся сделать это в Typescript:

Простой пример использования свойства компонента ListItem для указаниякак реализация ListItem. Уловка в Typescript состоит в том, чтобы использовать оператор распространения (...) в свойствах ListItem, чтобы проскользнуть мимо нежелательных и ненужных ошибок Typescript, которые указание этого типа компонента и свойств «to» для ListItem обычно вызывает (Typescript ошибка TS2769 Свойство 'компонент' не существует для типа 'IntrinsicAttributes ...)

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

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

или, в качестве альтернативы, если вы просто хотите использовать функцию onClick в ListItem, например:


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

`` ''

Это сработало для меня

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

Ответ, получивший наибольшее количество голосов, не удался после миграции MUI v4 (я на 4.3.1) из-за того, что «Функциональным компонентам нельзя давать ссылки. Попытки доступа к этой ссылке не удастся. Вы имели в виду использовать React.forwardRef ()?»

Я заменил компонент NavLink на оболочку следующим образом:

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

Применение:

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

Спасибо @HugoGresse Это отлично работает :-)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги