React MUI虚拟化Autocomplete选中选项后滚动回顶部问题求助
React MUI Autocomplete 多选时虚拟化列表滚动回顶部问题修复
问题描述
我在使用React MUI的Autocomplete组件,因为下拉列表需要承载数千条数据,所以集成了react-window实现虚拟化。虚拟化功能正常,但添加onChange函数后,多选时选中选项下拉列表会自动滚动回顶部。尝试过将ListboxComponent用useCallback包裹,但问题依然存在。
核心原因
useResetCache钩子在itemCount变化时调用resetAfterIndex(0, true),强制滚动到顶部。而多选时Autocomplete会重新渲染选项列表,触发这个钩子。ListboxComponent的useCallback依赖数组为空,但内部用到了renderRow和LISTBOX_PADDING,导致不必要的重渲染。
修复方案
1. 调整缓存重置的触发条件
修改useResetCache钩子,仅在分组切换(数据真正更新)时重置缓存,而非每次选项变化:
function useResetCache() { const ref = React.useRef<VariableSizeList>(null); React.useEffect(() => { if (ref.current != null) { ref.current.resetAfterIndex(0, true); } }, [selectedGroup]); // 仅在分组切换时重置缓存 return ref; }
2. 稳定renderRow函数
用useCallback包裹renderRow,避免每次渲染生成新函数触发重渲染:
const renderRow = React.useCallback((props: ListChildComponentProps) => { const { data, index, style } = props; const dataSet = data[index]; const inlineStyle = { ...style, top: (style.top as number) + LISTBOX_PADDING, }; const option = dataSet[1] as Product; return ( <li {...dataSet[0]} style={inlineStyle}> <Grid container spacing={1}> <Grid xs={12}> <Typography variant="subtitle2">{option.new_idprodotto}</Typography> </Grid> <Grid xs={12}> <Typography variant="body2"> {option.new_name} -{" "} { option[ "_nk_lineaprodotto_value@OData.Community.Display.V1.FormattedValue" ] } </Typography> </Grid> </Grid> </li> ); }, [LISTBOX_PADDING]);
3. 修正ListboxComponent的依赖数组
把renderRow和LISTBOX_PADDING加入useCallback的依赖数组,确保组件仅在必要时重渲染:
const ListboxComponent = React.useCallback< React.ForwardRefRenderFunction< HTMLDivElement, React.HTMLAttributes<HTMLElement> > >( function ListboxComponent(props, ref) { const { children, ...other } = props; const itemData: React.ReactNode[] = []; (children as React.ReactElement[]).forEach( (item: React.ReactElement & { children?: React.ReactElement[] }) => { itemData.push(item); itemData.push(...(item.children || [])); } ); const itemCount = itemData.length; const itemSize = 55; const getChildSize = () => { return itemSize; }; const getHeight = () => { if (itemCount > 8) { return 8 * itemSize; } return itemData.map(getChildSize).reduce((a, b) => a + b, 0); }; const gridRef = useResetCache(); // 不再传递itemCount参数 return ( <div ref={ref}> <OuterElementContext.Provider value={other}> <VariableSizeList itemData={itemData} height={getHeight() + 2 * LISTBOX_PADDING} width="100%" ref={gridRef} outerElementType={OuterElementType} innerElementType="ul" itemSize={() => itemSize} overscanCount={5} itemCount={itemCount} > {renderRow} </VariableSizeList> </OuterElementContext.Provider> </div> ); }, [renderRow, LISTBOX_PADDING] // 添加必要依赖 );
效果说明
修改后,多选时只会更新选中状态,不会触发不必要的滚动重置,同时避免了组件无意义的重渲染,彻底解决滚动回顶部的问题。
内容的提问来源于stack exchange,提问作者Nekte
相关产品推荐
相关产品推荐

