react-virtualized搭配InfiniteLoader等组件路由返回后无法恢复滚动位置
解决react-virtualized配合WindowScroller的滚动位置恢复问题
你之前的方案失效的核心原因是WindowScroller组件会接管List的滚动状态,你直接给List传scrollTop或者调用List的滚动方法,都会被WindowScroller的内部状态覆盖,自然达不到预期效果。以下是可直接落地的修改方案:
具体修改步骤
1. 新增恢复状态标记
在组件内加一个状态,用来判断是否已经完成滚动位置恢复,避免和WindowScroller的默认滚动逻辑冲突:
const [isPositionRestored, setIsPositionRestored] = useState(false)
2. 修改List的scrollTop属性
调整List的scrollTop赋值逻辑,未恢复位置前优先使用你存储的positionY,恢复后交还给WindowScroller控制:
<List // 其他原有属性保持不变 scrollTop={isPositionRestored ? scrollTop : positionY} />
3. 调整滚动恢复的触发逻辑
原来的useEffect依赖缺少数据加载完成的判断,容易出现数据还没加载、行高还没计算就触发滚动,导致位置偏移。修改如下:
useEffect(() => { // 确保数据加载完成、列表实例存在、有存储的滚动位置且未执行过恢复操作时再触发 if (listRef.current && positionY !== 0 && data.length > 0 && !isPositionRestored) { // 先重算所有行高,避免CellMeasurer缓存的旧高度导致滚动位置不准 listRef.current.recomputeRowHeights() // 标记恢复完成,后续滚动由WindowScroller接管 setIsPositionRestored(true) } }, [data, positionY, isPositionRestored])
4. 重置恢复标记
在页面卸载的清理逻辑中,重置恢复标记,保证下次进入页面可以重新触发恢复:
useEffect(() => { if (data.length === 0) { // 原有API调用逻辑 } return () => { const position = window.pageYOffset setPosition(position) // 新增重置逻辑 setIsPositionRestored(false) // 如果你的CellMeasurerCache是全局定义的,建议这里加一行清理缓存:cache.clearAll() } }, [])
备选方案
如果上述方案仍有问题,可以直接操作WindowScroller实例的滚动方法:
- 给WindowScroller加ref:
const windowScrollerRef = useRef() - 组件内使用时绑定ref:
<WindowScroller ref={windowScrollerRef}> - 恢复位置时调用:
windowScrollerRef.current?.scrollToPosition(positionY)
注意事项
你当前定义的CellMeasurerCache如果是写在组件外部的全局实例,切换页面后缓存的旧行高会导致滚动位置计算错误,建议要么把cache定义在组件内部,要么每次页面卸载时调用cache.clearAll()清理缓存,该方案兼容你正在使用的9.19.1版本react-virtualized。
内容的提问来源于stack exchange,提问作者gwrik09
相关产品推荐
相关产品推荐

