React-router-dom & material ui: проблема forwardRef

Хотя я применил forwardRef, как упоминалось в нескольких сообщениях и в примерах веб-сайтов materail-ui, у меня все еще есть предупреждение в консоли. И я действительно не понимаю, почему.

Я пытаюсь создать ящик со списком элементов, которые являются ссылками на реактивный маршрутизатор, встроенными в компонент ссылки Material-UI. Я встроил их, чтобы пользоваться созданной мной глобальной темой MUI.

PS: это машинописный код. Ref строгая типизация - это ад ;-)

ЗДЕСЬ НАХОДИТСЯ CODESANBOX ДЛЯ ВОСПРОИЗВЕДЕНИЯ ПРОБЛЕМЫ. Убедитесь, что страница браузераcodeandbox достаточно расширена, чтобы отобразить ящик

Компонент связи

import { Link as MUILink, LinkBaseProps } from "@material-ui/core";
import React from "react";
import {
  Link as LinkRouter,
  LinkProps as RouterLinkProps
} from "react-router-dom";

type RefLinkRouter =
  | ((instance: HTMLAnchorElement | null) => void)
  | React.RefObject<HTMLAnchorElement>
  | null
  | undefined;

type ILinkProps = RouterLinkProps;

const fwdLink = (props: ILinkProps, ref: RefLinkRouter): JSX.Element => (
  <LinkRouter ref={ref} {...props} />
);
const ForwardedLink = React.forwardRef<HTMLAnchorElement | null, ILinkProps>(
  fwdLink
);

const Link: React.FC<ILinkProps & LinkBaseProps> = (
  props: ILinkProps & LinkBaseProps
) => {
  return (
    <MUILink component={ForwardedLink} to={props.to} {...props}>
      {props.children}
    </MUILink>
  );
};

export { Link, ILinkProps };

Компонент ящика

import { Link } from "./../Link";
import { List, ListItem, makeStyles } from "@material-ui/core";
import Drawer from "@material-ui/core/Drawer";
import Hidden from "@material-ui/core/Hidden";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import MailIcon from "@material-ui/icons/Mail";
import InboxIcon from "@material-ui/icons/MoveToInbox";
import React from "react";

const drawerWidth = 240;

interface IOptionsValue {
  value: string;
  label: string;
}

interface IDrawerMenuItemsProps extends IOptionsValue {
  to: string;
  iconName?: JSX.Element;
}

interface IResponsiveDrawerProps {
  container?: number;
  menuItems: Array<IDrawerMenuItemsProps>;
}

const useStyles = makeStyles({
  list: {
    backgroundColor: "blue"
  },
  drawerMenuItemList: {
    color: "white",
    backgroundColor: "blue"
  }
});

const ResponsiveDrawer: React.FC<IResponsiveDrawerProps> = (
  props: IResponsiveDrawerProps
): JSX.Element => {
  // const { container } = props;
  const { menuItems } = props;

  const classes = useStyles();
  const [mobileOpen, setMobileOpen] = React.useState(false);

  const handleDrawerToggle = () => {
    setMobileOpen(!mobileOpen);
  };

  const drawer = (
    <div>
      <div />
      <List className={classes.drawerMenuItemList}>
        {menuItems.map((menuItem: IDrawerMenuItemsProps, index: number) => {
          return (
            <ListItem
              className={classes.drawerMenuItemList}
              key={menuItem.value}
              component={Link}
              to={menuItem.to}
            >
              <ListItemIcon className={classes.drawerMenuItemList}>
                {index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
              </ListItemIcon>
              <p className={classes.drawerMenuItemList}>{menuItem.label}</p>
            </ListItem>
          );
        })}
      </List>
    </div>
  );

  return (
    <>
      <Hidden smUp implementation="css">
        <Drawer
          //                    container={container}
          style={{ width: drawerWidth }}
          variant="temporary"
          anchor={"right"}
          open={mobileOpen}
          onClose={handleDrawerToggle}
          ModalProps={{
            keepMounted: true // Better open performance on mobile.
          }}
        >
          {drawer}
        </Drawer>
      </Hidden>
      <Hidden xsDown implementation="css">
        <Drawer variant="permanent" open>
          {drawer}
        </Drawer>
      </Hidden>
    </>
  );
};

export default ResponsiveDrawer;

Домашняя страница, на которой я называю ящик

import React from "react";
import ResponsiveDrawer from "./../ResponsiveDrawer";

const HomePage = () => {
  const menuItems = [
    {
      label: "Menu 1",
      value: "menu1",
      to: "/app"
    },
    {
      label: "Menu 2",
      value: "menu2",
      to: "/app"
    }
  ];

  return (
    <>
      <ResponsiveDrawer menuItems={menuItems} />
      <div>here were are</div>
    </>
  );
};

export default HomePage;

введите здесь описание изображения


person Jerome    schedule 26.04.2020    source источник


Ответы (1)


Согласно документации, у нас есть некоторые предостережения при использовании Refs.

https://material-ui.com/guides/composition/#caveat-with-refs

Однако с учетом того, как вы это реализуете, я понимаю, что ваша идея состоит в том, чтобы воспользоваться преимуществом использования компонента Link в Material-UI, имеющего возможности маршрутизатора реакции и использующего компонент Children.

В этом случае вы можете использовать свойство «component», но для этого вы должны использовать React.forwardRef для пересылки ссылки для компонента, чтобы иметь доступ к узлу DOM.

https://codesandbox.io/s/keen-banzai-o2q24

    type LinkProps = LinkBaseProps & RouterLinkProps;

    const LinkBehavior = React.forwardRef<any, ILinkProps>((props, ref) => (
       <MUILink component={LinkRouter} ref={ref} to={props.to} {...props} />
    ));

    export { LinkBehavior as Link };
    export type ILinkProps = LinkProps;
person Jairon Alves Lima    schedule 26.04.2020
comment
спасибо большое, очень ясно, я сделал наоборот. - person Jerome; 27.04.2020