0

我正在尝试使用我编写的这个组件:

import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
import ListItemText from '@material-ui/core/ListItemText';

const ITEMS = {
  item1: { id: 1, name: '1', description: 'item1', protected: true },
  item2: { id: 2, name: '2', description: 'item2' },
  item3: { id: 3, name: '3', description: 'item3' },
}

// See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20356#issuecomment-435708501
// for an explanation on why the component return type was cast to `any` below.
const MenuItemList: any = () => {
  return Object.values(ITEMS).map(
    (item) =>
      !item.protected && (
        <MenuItem key={item.id} value={item.id}>
          <ListItemText
            primary={item.name}
            secondary={item.description}
          />
        </MenuItem>
      )
  );
};

export default MenuItemList;

...一次在Textfield类型内select,另一次在Menu组件内。但是,在浏览器中访问它时出现以下错误:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

关于如何解决这个问题的任何想法?

更新:

这是我如何调用这个组件:

<Menu
  id={id}
  open={open}
  anchorEl={anchorEl}
  getContentAnchorEl={null}
  keepMounted={false}
  onClose={handleClose}
  elevation={2}
  PaperProps={{
      square: true,
  }}
  anchorOrigin={{
      vertical: 'bottom',
      horizontal: 'right',
  }}
  transformOrigin={{
      vertical: 'top',
      horizontal: 'right',
  }}
>
  < MenuItemList />
</Menu>

<Field
  name="items"
  label="Select Item"
  padding={2}
  component={TextField}
  select
  fullWidth
  SelectProps={{
    MenuProps: {
      elevation: 2,
      getContentAnchorEl: null,
      anchorOrigin: {
        vertical: 'bottom',
        horizontal: 'left',
      },
      transformOrigin: {
        vertical: 'top',
        horizontal: 'left',
      },
    },
    IconComponent: ExpandMoreIcon,
  }}
  variant="filled"
  InputProps={{
    disableUnderline: true,
  }}
>
  <MenuItemList />
</Field>

更新 2

这是错误堆栈:

Check the render method of `ForwardRef(Menu)`.
    in MenuItemList (at ShowItemsDialog.tsx:105)
    in ul (created by ForwardRef(List))
    in ForwardRef(List) (created by WithStyles(ForwardRef(List)))
    in WithStyles(ForwardRef(List)) (created by ForwardRef(MenuList))
    in ForwardRef(MenuList) (created by ForwardRef(Menu))
    in div (created by ForwardRef(Paper))
    in ForwardRef(Paper) (created by WithStyles(ForwardRef(Paper)))
    in WithStyles(ForwardRef(Paper)) (created by Transition)
    in Transition (created by ForwardRef(Grow))
    in ForwardRef(Grow) (created by TrapFocus)
    in TrapFocus (created by ForwardRef(Modal))
    in div (created by ForwardRef(Modal))
    in ForwardRef(Portal) (created by ForwardRef(Modal))
    in ForwardRef(Modal) (created by ForwardRef(Popover))
    in ForwardRef(Popover) (created by WithStyles(ForwardRef(Popover)))
    in WithStyles(ForwardRef(Popover)) (created by ForwardRef(Menu))
    in ForwardRef(Menu) (created by WithStyles(ForwardRef(Menu)))
    in WithStyles(ForwardRef(Menu)) (created by ForwardRef(SelectInput))
    in ForwardRef(SelectInput) (created by ForwardRef(InputBase))
    in div (created by ForwardRef(InputBase))
    in ForwardRef(InputBase) (created by WithStyles(ForwardRef(InputBase)))
    in WithStyles(ForwardRef(InputBase)) (created by ForwardRef(FilledInput))
    in ForwardRef(FilledInput) (created by WithStyles(ForwardRef(FilledInput)))
    in WithStyles(ForwardRef(FilledInput)) (created by ForwardRef(Select))
    in ForwardRef(Select) (created by WithStyles(ForwardRef(Select)))
    in WithStyles(ForwardRef(Select)) (created by ForwardRef(TextField))
    in div (created by ForwardRef(FormControl))
    in ForwardRef(FormControl) (created by WithStyles(ForwardRef(FormControl)))
    in WithStyles(ForwardRef(FormControl)) (created by ForwardRef(TextField))
    in ForwardRef(TextField) (created by WithStyles(ForwardRef(TextField)))
    in WithStyles(ForwardRef(TextField)) (created by FormikMaterialUITextField)
    in FormikMaterialUITextField (created by Field)
    in Field (at ShowItemsDialog.tsx:77)
4

2 回答 2

1

似乎问题不在此组件上。你能粘贴你使用的组件的代码MenuItemList吗?似乎是Material-ui 使用的ref react的问题

我还建议使用filter [doc]来过滤受保护的项目。

import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
import ListItemText from '@material-ui/core/ListItemText';

const ITEMS = {
  item1: { id: 1, name: '1', description: 'item1', protected: true },
  item2: { id: 2, name: '2', description: 'item2' },
  item3: { id: 3, name: '3', description: 'item3' },
}

const MenuItemList: any = () => {
  return Object.values(ITEMS).filter(item => !item.protected).map(
    (item) =>
        <MenuItem key={item.id} value={item.id}>
          <ListItemText
            primary={item.name}
            secondary={item.description}
          />
        </MenuItem>
  );
};

export default MenuItemList;

编辑

Menu 使用 Menu 的第一个子项作为 Menu 内部使用的 Popover 组件的“内容锚”。“内容锚点”是菜单内的 DOM 元素,Popover 尝试与锚点元素(菜单外的元素,作为定位菜单的参考点)对齐。

为了利用第一个子元素作为内容锚点,Menu 向它添加了一个 ref(使用 cloneElement)。为了不收到您收到的错误(并使定位正常工作),您的功能组件需要将 ref 转发到它呈现的组件之一(通常是最外面的组件 - 在您的情况下为 div )。

当您使用 div 作为 Menu 的直接子级时,您不会收到错误消息,因为 div 可以成功接收 ref。

因此,您应该将代码更改MenuItemList为:

import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
import ListItemText from '@material-ui/core/ListItemText';

const ITEMS = {
  item1: { id: 1, name: '1', description: 'item1', protected: true },
  item2: { id: 2, name: '2', description: 'item2' },
  item3: { id: 3, name: '3', description: 'item3' },
}

const MenuItemList: any = () => {
  return (
    <div>
      {Object.values(ITEMS)
        .filter(item => !item.protected)
        .map(item => (
          <MenuItem key={item.id} value={item.id}>
            <ListItemText primary={item.name} secondary={item.description} />
          </MenuItem>
        ))}
    </div>
  );
};

export default MenuItemList;
于 2020-04-23T08:38:38.960 回答
1

stefano.orlando 的解释似乎是正确的,直到他们提出解决方案。将 div 包裹在其他组件周围不会导致 refs 被转发。为此,您需要使用 React.forwardRef。下面的代码将使得如果一个 ref 被传递到MenuItemList,该 ref 将被重定向到第一个MenuItem内部MenuItemList

import React from 'react';
import MenuItem from '@material-ui/core/MenuItem';
import ListItemText from '@material-ui/core/ListItemText';

const ITEMS = {
  item1: { id: 1, name: '1', description: 'item1', protected: true },
  item2: { id: 2, name: '2', description: 'item2' },
  item3: { id: 3, name: '3', description: 'item3' },
}

const MenuItemList: any = React.ForwardRef((props, ref) => {
  return (
    Object.values(ITEMS)
      .filter(item => !item.protected)
      .map((item, index) => (
        <MenuItem 
          ref={index === 0 ? ref : undefined}
          key={item.id} 
          value={item.id}
        >
          <ListItemText primary={item.name} secondary={item.description} />
        </MenuItem>
      ))
  );
});

export default MenuItemList;
于 2020-04-23T13:56:30.810 回答