React虚拟化消息容器添加新消息后滚动至最后一条的问题
React虚拟化消息容器滚动问题解决建议
实现React虚拟化消息容器时,添加新消息后无法自动滚动到最后一条,试过scrollToIndex、scrollToRow、useRef方案都没效果,求可行解决办法。
原代码
useEffect(() => { parentRef.current!?.lastElementChild?.scrollIntoView() }, [messagesList]) const rowRenderer = useCallback( ({index, isScrolling, key, parent, style, scrollToRow}) => { const chat = messagesList[index] console.log( 'cell renderer index : ', index, ' list length : ', messagesList.length ) return ( <CellMeasurer cache={cache} columnIndex={0} key={key} parent={parent} rowIndex={index} className='List' scrollToRow={messagesList.length - 1} autoHeight={true} autoWidth={true} // ref={parentRef} recomputeRowHeights={index} recomputeRowWidths={index} resetMeasurementForRow={index} resetMeasurementForColumn={index} resetMeasurements={true} style={{wordWrap: 'break-word', whiteSpace: 'normal', flexWrap: 'wrap'}}> {({measure, registerChild}: any) => ( <div style={style}> <Message key={chat?.id} ref={registerChild} data={chat} /> </div> )} </CellMeasurer> ) }, [messagesList, cache] ) const getMessages = useMemo(() => { console.log('getting messages....') return ( <List scrollToAlignment={'end'} height={700} width={wrapperRef.current ? wrapperRef.current?.clientWidth : 100} style={{width: '100%', border: '2px solid grey'}} // important to maintain the width itemSize={10} deferredMeasurementCache={cache} rowHeight={cache.rowHeight} rowCount={messagesList.length} rowRenderer={rowRenderer} // scrollToIndex={messagesList.length} // ref={parentRef} scrollToRow={messagesList.length - 1} // scrollToIndex /> ) }, [messagesList, wrapperRef.current])
解决建议
用List组件的ref调用官方API
虚拟化列表的scrollIntoView经常失效,因为未渲染的元素不在DOM里。直接用react-virtualized提供的scrollToIndex更可靠:- 给List加ref:
const listRef = useRef(null); - 在
messagesList更新的useEffect里,用requestAnimationFrame确保DOM更新后执行滚动:useEffect(() => { if (messagesList.length > 0 && listRef.current) { requestAnimationFrame(() => { listRef.current.scrollToIndex({ index: messagesList.length - 1, align: 'end' }); }); } }, [messagesList]);
- 给List加ref:
清理CellMeasurer的无效props
你在CellMeasurer里加了很多不属于它的props(比如scrollToRow、recomputeRowHeights),这些是List的属性,会干扰组件逻辑,全部删掉。只保留cache、columnIndex、parent、rowIndex这些必要属性。确保缓存初始化正确
检查deferredMeasurementCache是否用createCellMeasurerCache正确创建,比如:const cache = useMemo(() => createCellMeasurerCache({ defaultHeight: 100, // 设一个合理的默认高度 fixedWidth: true }), []);错误的缓存配置会导致高度计算偏差,滚动位置出错。
修正List的冗余配置
删掉itemSize、rowHeight这些和deferredMeasurementCache冲突的属性,因为启用缓存后,List会自动从缓存获取行高。
修改后的完整代码示例
import { List, CellMeasurer, CellMeasurerCache } from 'react-virtualized'; // 正确初始化缓存 const cache = useMemo(() => createCellMeasurerCache({ defaultHeight: 100, fixedWidth: true }), []); const listRef = useRef(null); const wrapperRef = useRef(null); useEffect(() => { if (messagesList.length > 0 && listRef.current) { requestAnimationFrame(() => { listRef.current.scrollToIndex({ index: messagesList.length - 1, align: 'end' }); }); } }, [messagesList]); const rowRenderer = useCallback( ({ index, isScrolling, key, parent, style }) => { const chat = messagesList[index]; return ( <CellMeasurer cache={cache} columnIndex={0} key={key} parent={parent} rowIndex={index} > {({ measure, registerChild }) => ( <div style={style} ref={registerChild} onLoad={measure}> <Message key={chat?.id} data={chat} /> </div> )} </CellMeasurer> ); }, [messagesList, cache] ); const getMessages = useMemo(() => ( <List ref={listRef} scrollToAlignment="end" height={700} width={wrapperRef.current?.clientWidth || 100} style={{ width: '100%', border: '2px solid grey' }} deferredMeasurementCache={cache} rowCount={messagesList.length} rowRenderer={rowRenderer} /> ), [messagesList, wrapperRef.current, cache, rowRenderer]);
内容的提问来源于stack exchange,提问作者Vismay Yerunkar
相关产品推荐
相关产品推荐

