react-virtuoso虚拟列表中已渲染项的瞬时高亮问题
解决方案
针对你遇到的视口内项无法触发高亮的问题,核心原因是useRef的变更不会触发组件重渲染,导致已在视口中的ItemRow无法更新高亮相关props。以下提供两种可行的修复方案:
方案一:使用useState跟踪高亮ID(推荐)
改用状态存储需要高亮的项ID,利用状态变更触发组件重渲染,让所有ItemRow同步更新高亮状态:
修改MyList组件
function MyList(props) { const { collection } = props; const { getLastId } = useApiResultsContext(); const listRef = useRef(null); // 用状态存储当前高亮项的ID(唯一标识,避免索引变化的问题) const [highlightedItemId, setHighlightedItemId] = useState(null); useEffect(() => { const newItemId = getLastId(); if (!newItemId) return; // 设置高亮ID setHighlightedItemId(newItemId); // 滚动到目标项 const targetIndex = collection.findIndex(item => item.id === newItemId); if (targetIndex >= 0) { listRef.current?.scrollToIndex({ index: targetIndex, align: 'start' }); } // 1秒后自动取消高亮 const timer = setTimeout(() => { setHighlightedItemId(null); }, 1000); return () => clearTimeout(timer); }, [collection, getLastId]); return ( <Virtuoso ref={listRef} data={collection} itemContent={(index, item) => ( <ItemRow content={item} isHighlighted={item.id === highlightedItemId} /> )} /> ); }
修改ItemRow组件
function ItemRow(props) { const { content, isHighlighted } = props; return ( <div className={isHighlighted ? 'highlight' : undefined}> {/* 列表项内容渲染 */} </div> ); }
此方案逻辑清晰,利用React状态驱动视图更新,无需手动强制渲染,兼容性更好。
方案二:调用Virtuoso的forceUpdate强制更新视口内项
如果你坚持使用useRef,可以通过Virtuoso实例的forceUpdate方法,强制视口内的项重新渲染,从而更新高亮状态:
修改MyList组件
function MyList(props) { const { collection } = props; const { getLastId } = useApiResultsContext(); const highlightIndex = useRef(); const listRef = useRef(null); useEffect(() => { const newItemId = getLastId(); if (!newItemId) return; const targetIndex = collection.findIndex(item => item.id === newItemId); if (targetIndex < 0) return; // 滚动到目标项 listRef.current?.scrollToIndex({ index: targetIndex, align: 'start' }); // 设置高亮索引并强制列表更新 highlightIndex.current = targetIndex; listRef.current?.forceUpdate(); // 1秒后取消高亮并再次强制更新 const timer = setTimeout(() => { highlightIndex.current = undefined; listRef.current?.forceUpdate(); }, 1000); return () => clearTimeout(timer); }, [collection, getLastId]); return ( <Virtuoso ref={listRef} data={collection} itemContent={(index, item) => ( <ItemRow content={item} isHighlighted={index === highlightIndex.current} /> )} /> ); }
修改ItemRow组件
function ItemRow(props) { const { content, isHighlighted } = props; return ( <div className={isHighlighted ? 'highlight' : undefined}> {/* 列表项内容渲染 */} </div> ); }
此方案通过Virtuoso的API强制更新,避免了状态变更带来的全局重渲染,但依赖组件库的特定方法。
补充说明
你之前使用useState导致功能失效,大概率是因为旧逻辑中高亮状态与滚动逻辑的时机冲突(比如状态更新导致列表重渲染时,滚动操作还未完成)。方案一中通过将滚动与状态设置放在同一个useEffect,并利用定时器自动取消高亮,解决了这个问题。
内容的提问来源于stack exchange,提问作者user2921009
相关产品推荐
相关产品推荐

