微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

MUI 选择自定义 MenuItem 无法正常工作

如何解决MUI 选择自定义 MenuItem 无法正常工作

当 MUI 的 MenuItemSelect 结合并在单独的组件中呈现时,我遇到了问题。

这是codesandbox

基本上,我有这样的事情:

import { Select } from "@material-ui/core";
import CustomMenuItem from "./CustomMenuItem";
import React from "react";

export default function App() {
  const userIds = [1,2,3];
  return (
    <Select
      id="user"
      name="User"
      onChange={(event: React.ChangeEvent<{ value: unkNown }>) => {
        alert(event.target.value as number);
      }}
    >
      {userIds.map((userId) => (
        <CustomMenuItem key={userId} userId={userId} />
      ))}
    </Select>
  );
}

这是自定义项目:

import { MenuItem,Typography } from "@material-ui/core";
import React from "react";

interface CustomMenuItemProps {
  userId: number;
}

const CustomMenuItem = React.forwardRef<HTMLLIElement,CustomMenuItemProps>(
  (props: CustomMenuItemProps,ref) => {
    const { userId,...rest } = props;
    return (
      <MenuItem value={userId} {...rest} ref={ref}>
        <Typography>{userId}</Typography>
      </MenuItem>
    );
  }
);
export default CustomMenuItem;

起初,我在没有任何 ref 的情况下完成了此操作,但这在控制台 (Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?) 中给了我一个错误,因此在谷歌搜索一段时间后,我发现我必须通过此 ref。我还传递了道具的 ...rest,因为我知道 MenuItem 需要它们。

预期行为:当我点击 MenuItem 时,它会在 Select 组件中被选中。

实际行为:没有任何反应。

问题是,我制作了 CustomMenuItem 以使其可重复使用。但在此之前,我有一个简单的函数,例如:renderItem,我在 Select.renderValueuserIds.map 中都使用过它,它的代码CustomMenuItem 相同 - 它返回相同JSX 树。它当时有效,但现在由于某种原因不起作用。所以,如果我愿意:

    <Select
      id="user"
      name="User"
      onChange={(event: React.ChangeEvent<{ value: unkNown }>) => {
        alert(event.target.value as number);
      }}
    >
      {userIds.map((userId) => (
        <MenuItem key={userId} value={userId}>
          <Typography>{userId}</Typography>
        </MenuItem>
      ))}
    </Select>

它很简单:(

在这里遗漏了什么吗?

解决方法

Select 的一些实现细节妨碍了尝试以这种方式自定义 MenuItem

Select uses the value prop 的直接子代。在你的情况下 Select 的直接子元素是 CustomMenuItem 元素,它只有一个 userId 道具——而不是一个 value 道具;因此,当您单击自定义菜单项之一时,Select 会找到 undefined 作为新值。

您可以通过将 userId 道具复制为 value 道具来解决此问题:

import { Select } from "@material-ui/core";
import CustomMenuItem from "./CustomMenuItem";
import React from "react";

export default function App() {
  const userIds = [1,2,3];
  const [value,setValue] = React.useState(1);
  console.log("value",value);
  return (
    <Select
      id="user"
      name="User"
      value={value}
      onChange={(event: React.ChangeEvent<{ value: unknown }>) => {
        setValue(event.target.value as number);
      }}
    >
      {userIds.map((userId) => (
        <CustomMenuItem key={userId} value={userId} userId={userId} />
      ))}
    </Select>
  );
}

Edit MUI Select custom MenuItem

如果您查看控制台日志,这将成功更改 Select 的值。新值成功显示,因为我稍后会解释一个单独的问题。

您可能会想“那么我可以只使用 value 道具而不是 userId 道具,而不是同时使用两者”,但 value 道具实际上不会到达您的自定义组件. Select 使用 React.cloneElementchange the value prop to undefined,而是将它放在 data-value 中以避免在最终 html 中指定 value 道具(这将不是有效的被渲染的 html 元素的属性)。

在我上面的沙箱中,您会注意到当您选择一个值时,新值没有成功显示为所选值。这是因为 Select uses the children prop of the selected child 作为显示值,除非您指定 renderValue propchildren 元素的 CustomMenuItem 属性未定义。

您可以通过在 renderValue 上使用 Select 道具或再次将 userId 指定为子项来解决此问题:

import { Select } from "@material-ui/core";
import CustomMenuItem from "./CustomMenuItem";
import React from "react";

export default function App() {
  const userIds = [1,value);
  return (
    <Select
      id="user"
      name="User"
      value={value}
      onChange={(event: React.ChangeEvent<{ value: unknown }>) => {
        setValue(event.target.value as number);
      }}
    >
      {userIds.map((userId) => (
        <CustomMenuItem key={userId} value={userId} userId={userId}>
          {userId}
        </CustomMenuItem>
      ))}
    </Select>
  );
}

Edit MUI Select custom MenuItem (forked)

这有效,但也会删除自定义菜单项组件试图提供的所有值。我认为实现这一点的最简单方法(同时仍然与 Material-UI Select 设计配合良好)是将可重用代码放在用于呈现菜单项的函数中,而不是制作自定义菜单项组件:>

import { Select } from "@material-ui/core";
import React from "react";
import { MenuItem,Typography } from "@material-ui/core";

const renderMenuItem = (value: number) => {
  return (
    <MenuItem key={value} value={value}>
      <Typography>{value}</Typography>
    </MenuItem>
  );
};

export default function App() {
  const userIds = [1,value);
  return (
    <Select
      id="user"
      name="User"
      value={value}
      onChange={(event: React.ChangeEvent<{ value: unknown }>) => {
        setValue(event.target.value as number);
      }}
    >
      {userIds.map(renderMenuItem)}
    </Select>
  );
}

Edit MUI Select custom MenuItem

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。