react-window FixedSizeList搭配TextField select选后菜单无法关闭
问题根因
带select属性的MUI TextField,内部实际渲染的是MUI Select组件,默认逻辑是监听直接子级MenuItem的点击事件,自动完成值更新、下拉菜单关闭的联动。当使用react-window的<FixedSizeList>做虚拟滚动时,MenuItem被嵌套在虚拟列表的多层容器内部,不符合Select组件默认的事件监听路径,因此原生联动逻辑失效:哪怕手动触发onChange更新了值,组件也无法感知选中完成的时机,自然不会自动关闭下拉。
修复方案
不需要硬改原生事件逻辑,利用MUI Select提供的自定义子节点渲染能力,拿到组件内部维护的下拉关闭方法,选中选项时主动调用即可,核心操作如下:
- 把
<FixedSizeList>作为Select的子节点,以渲染函数的形式传入,MUI会自动给这个函数注入onClose方法,用来手动控制下拉关闭 - 虚拟列表渲染
MenuItem时,在点击回调里先更新选中值,再调用注入的onClose方法,即可复现原生选中后自动关下拉的效果 - 配置对应的菜单高度、行高参数适配虚拟滚动的尺寸要求
参考实现代码:
import { useState } from 'react'; import { TextField, MenuItem } from '@mui/material'; import { FixedSizeList } from 'react-window'; const OPTIONS = Array.from({ length: 10000 }).map((_, idx) => ({ label: `测试选项 ${idx}`, value: `option_${idx}` })); const VirtualListRow = ({ index, style, data }) => { const { options, handleSelect, closeDropdown } = data; const currentOption = options[index]; return ( <MenuItem style={style} value={currentOption.value} onClick={() => { handleSelect(currentOption.value); closeDropdown(); }} > {currentOption.label} </MenuItem> ); }; export default function VirtualSelect() { const [selected, setSelected] = useState(''); return ( <TextField style={{ width: 320 }} select label="虚拟滚动选择框" value={selected} onChange={(e) => setSelected(e.target.value)} SelectProps={{ MenuProps: { PaperProps: { style: { maxHeight: 'none' } } } }} > {({ onClose }) => ( <FixedSizeList width="100%" height={300} itemCount={OPTIONS.length} itemSize={48} itemData={{ options: OPTIONS, handleSelect: setSelected, closeDropdown: onClose }} > {VirtualListRow} </FixedSizeList> )} </TextField> ); }
注意:该方案适配MUI v5及以上版本,不需要额外维护Select的
open状态,完全复用组件内部的状态逻辑,不会出现点击外部区域无法关下拉、键盘操作失效等兼容性问题。
内容的提问来源于stack exchange,提问作者Oron Bendavid
相关产品推荐
相关产品推荐

