reactjs - MUI 选择自定义 MenuItem 无法正常工作
问题描述
在将 MUI 与单独的组件MenuItem
结合并将其呈现时,我遇到了一个问题。Select
这是代码框
基本上,我有这样的事情:
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;
起初,我在没有任何引用的情况下完成了此操作,但这在控制台 ( Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
) 中给了我一个错误,所以在谷歌搜索了一段时间后,我发现我必须通过这个引用。我也传递了...rest
道具,因为我知道MenuItem
需要它们。
预期行为:当我单击 时MenuItem
,它会在Select
组件中被选中。
实际行为:没有任何反应。
问题是,我制作了CustomMenuItem
它以使其可重复使用。但在此之前,我有一个简单的函数,比如:renderItem
我在 inSelect.renderValue
和 in 中都使用userIds.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
使用其直系孩子的价值道具。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>
);
}
Select
如果您查看控制台日志,这将成功更改 的值。由于另一个问题,我稍后会解释新值未成功显示。
您可能会想“那么我可以只使用value
prop 而不是userId
prop 而不是两者都使用”,但value
prop 实际上不会到达您的自定义组件。Select
用于React.cloneElement
将value prop 更改为 undefined,而是将其放入data-value
以避免value
在最终 html 中指定 prop (这对于呈现的 html 元素不是有效属性)。
在我上面的沙箱中,您会注意到,当您选择一个值时,新值没有成功显示为所选值。这是因为除非您指定renderValue 属性,否则Select
使用所选子项的 children 属性作为显示值。元素的prop未定义。children
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, 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}>
{userId}
</CustomMenuItem>
))}
</Select>
);
}
这有效,但也删除了自定义菜单项组件试图提供的所有值。我认为实现这一点的最简单方法(同时仍然适用于 Material-UISelect
设计)是将可重用代码放入用于呈现菜单项的函数中,而不是制作自定义菜单项组件:
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, 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(renderMenuItem)}
</Select>
);
}
推荐阅读
- r - 如何在 R 中创建一个用户生成的函数,将列中的所有值转换为日期格式?
- firebase - Flutter/Firebase 错误 - 未处理的异常:类型 '_CompactLinkedHashSet
' 不是类型 'FutureOr 的子类型 >>' - java - AltUnityDriver create-无法执行命令'mobiledevice tunnel -u (device udid)13000 13000'
- blazor - Blazor - 在 RenderFragment 部分刷新子组件
- shell - 如何将变量从一个目录存储或捕获到另一个目录并在 Linux 中使用 cut 命令执行
- reactjs - 在 Windows 上运行多个反应应用程序时无法识别端口
- python - ImportError:Apache 中没有名为 cx_Oracle 的模块
- r - 使用 R 中的 purr 包匹配和分析数据
- spring - Springboot 从 S3 上传和下载文件(多种格式)
- python - 如何将自定义装饰器添加到 FastAPI 路由?