如何将react-virtual的useVirtual与Material-UI Select集成实现选项虚拟化
Material-UI Select 大量选项卡顿优化方案
问题背景
当Material-UI Select的选项数量超过200条时会出现卡顿,属于官方已知问题,原因是默认会全量渲染所有MenuItem节点,大量DOM节点占用内存导致交互卡顿。
现有react-virtual实现的修复
你当前的useVirtual实现存在几处可优化的点,调整后即可正常运行:
- ref绑定对象错误:你将
parentRef绑定到了Menu根容器,实际滚动容器是内部的MenuList,应该把ref绑定到MenuListProps的ref属性上 - 值类型不匹配:初始state的value是数字类型,而MenuItem的value被转成了字符串,导致初始选中状态不生效
- 缺少选中项自动定位逻辑:打开下拉时不会自动滚动到当前选中的选项位置
修正后的完整代码如下:
import * as React from "react"; import Select from "@material-ui/core/Select"; import MUIMenuItem from "@material-ui/core/MenuItem"; import { useVirtual } from "react-virtual"; export default function Demo() { const parentRef = React.useRef<HTMLDivElement>(null); const [value, setValue] = React.useState<string>("1"); // 调整为字符串类型和MenuItem的value匹配 const rowVirtualizer = useVirtual({ size: 10000, parentRef, estimateSize: React.useCallback(() => 35, []), overscan: 5 }); // 可选:下拉打开时自动滚动到选中项 React.useEffect(() => { if (parentRef.current && value) { const targetIndex = Number(value); rowVirtualizer.scrollToIndex(targetIndex); } }, [value, rowVirtualizer]); return ( <Select MenuProps={{ style: { width: `400px`, }, anchorOrigin: { vertical: "bottom", horizontal: "left" }, getContentAnchorEl: null, MenuListProps: { ref: parentRef, // ref绑定到MenuList,也就是实际滚动容器 style: { height: "200px", // 滚动容器高度放在MenuList上 overflow: "auto", width: "100%", position: "relative" } } }} value={value} onChange={(e: React.ChangeEvent<{ value: unknown }>) => setValue(e.target.value as string)} > <div style={{ height: `${rowVirtualizer.totalSize}px`, position: "relative" }}> {rowVirtualizer.virtualItems.map(({ index, size, start }) => ( <MUIMenuItem style={{ position: "absolute", top: 0, left: 0, width: "100%", height: `${size}px`, transform: `translateY(${start}px)` }} key={index} value={String(index)} > <div>{index}</div> </MUIMenuItem> ))} </div> </Select> ); }
其他可选实现方案
- 使用
react-window实现虚拟化:Material-UI官方提供了基于react-window的虚拟化Select示例,适配性更强,无需手动处理定位逻辑 - 升级到支持原生虚拟化的MUI X Select组件:高阶版本的MUI X Select内置了虚拟化支持,只需配置
virtualize属性即可开启 - 搭配搜索过滤功能:如果选项量超过10万条,可以在Select顶部加搜索输入框,先过滤缩小选项范围再渲染,进一步降低渲染压力
内容的提问来源于stack exchange,提问作者Itay Tur
相关产品推荐
相关产品推荐

