Material UI Autocomplete组件传入大数据集时渲染延迟问题
Material UI Autocomplete 大数据集渲染延迟问题
在React应用中使用Material UI的Autocomplete组件时,传入约10000条数据的大数据集作为props,下拉菜单渲染出预期数据前会出现约5秒的显著延迟。以下是按照官方文档实现的组件代码:
import * as React from 'react'; import Autocomplete from '@mui/material/Autocomplete'; import TextField from '@mui/material/TextField'; import Checkbox from '@mui/material/Checkbox'; import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; const icon = <CheckBoxOutlineBlankIcon fontSize="small" />; const checkedIcon = <CheckBoxIcon fontSize="small" />; type AutoSelectDropdownProps = { dropdownData?: any[], isMultiple?: boolean, onChange?: (event: { target: { value: any[]; name?: undefined } }) => void defaultvalue?: any[], valueField?: string displayField?: string, } export function AutoCompleteDropDown(props: AutoSelectDropdownProps) { let newObj = [...props.dropdownData]; const loadValues = (event: any, values: any[]) => { const target = { value: [], name: undefined }; if (values && values.length > 0) { target.value = values.map((item: any) => item[props.valueField || 'value']); } props.onChange({target}); } return ( <Autocomplete multiple options={newObj} getOptionLabel={(option) => option.value} limitTags={2} onChange={loadValues} defaultValue={ props.dropdownData.filter((item: any) => { return props.defaultvalue.includes(item[props.valueField || 'value']); }) } renderOption={(props, option, { selected }) => ( <li {...props}> <Checkbox icon={icon} checkedIcon={checkedIcon} checked={selected} /> {option.value} </li> )} size="small" sx={{ minWidth: 180, width: '100%', mt: 1, backgroundColor: "white" }} renderInput={(params) => ( <TextField {...params} size='small' /> )} /> ); } export default AutoCompleteDropDown;
优化方案
- 启用虚拟滚动:使用虚拟滚动组件只渲染可视区域内的选项,大幅减少DOM节点数量,避免全量渲染导致的阻塞。
- 移除无意义数据拷贝:删除
let newObj = [...props.dropdownData];,直接使用props.dropdownData作为数据源,减少内存开销。 - 缓存默认值计算结果:用
useMemo包裹默认值的过滤逻辑,避免每次组件渲染都重复执行计算。 - 优化字段读取逻辑:统一使用
displayField和valueField指定的字段,避免硬编码option.value,同时保证字段访问的一致性。 - 减少组件重渲染:用
React.memo包裹Checkbox组件,避免选项状态变化时不必要的重渲染。
修改后的优化代码
import * as React from 'react'; import Autocomplete from '@mui/material/Autocomplete'; import TextField from '@mui/material/TextField'; import Checkbox from '@mui/material/Checkbox'; import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; import { FixedSizeList } from 'react-window'; const icon = <CheckBoxOutlineBlankIcon fontSize="small" />; const checkedIcon = <CheckBoxIcon fontSize="small" />; // 缓存Checkbox组件减少重渲染 const MemoizedCheckbox = React.memo((props: { checked: boolean }) => ( <Checkbox icon={icon} checkedIcon={checkedIcon} checked={props.checked} /> )); type AutoSelectDropdownProps = { dropdownData?: any[], isMultiple?: boolean, onChange?: (event: { target: { value: any[]; name?: undefined } }) => void defaultvalue?: any[], valueField?: string displayField?: string, } export function AutoCompleteDropDown(props: AutoSelectDropdownProps) { const valueField = props.valueField || 'value'; const displayField = props.displayField || 'value'; // 缓存默认值计算结果 const defaultValue = React.useMemo(() => { if (!props.dropdownData || !props.defaultvalue) return []; return props.dropdownData.filter(item => props.defaultvalue.includes(item[valueField]) ); }, [props.dropdownData, props.defaultvalue, valueField]); const loadValues = (event: any, values: any[]) => { const target = { value: values?.map(item => item[valueField]) || [], name: undefined }; props.onChange?.({target}); } // 虚拟滚动列表组件 const ListboxComponent = React.useMemo(() => ({ children, ...other }) => ( <FixedSizeList {...other} height={300} itemSize={40} itemCount={children.length} > {({ index, style }) => ( <div style={style}>{children[index]}</div> )} </FixedSizeList> ), []); return ( <Autocomplete multiple options={props.dropdownData || []} getOptionLabel={(option) => option[displayField]} limitTags={2} onChange={loadValues} defaultValue={defaultValue} renderOption={(props, option, { selected }) => ( <li {...props}> <MemoizedCheckbox checked={selected} /> {option[displayField]} </li> )} size="small" sx={{ minWidth: 180, width: '100%', mt: 1, backgroundColor: "white" }} renderInput={(params) => ( <TextField {...params} size='small' /> )} ListboxComponent={ListboxComponent} /> ); } export default AutoCompleteDropDown;
内容的提问来源于stack exchange,提问作者Yash Chauhan
相关产品推荐
相关产品推荐

