如何用react-window/react-vizualized实现双动态高度列表混排布局?
嘿,这个需求我之前帮人解决过,刚好能用react-window或者react-vizualized来实现——毕竟react-vizualized本身就是基于react-window封装的,核心思路大同小异。下面给你具体的实现方案,都是围绕共享同一个滚动容器+动态计算每个列表项的位置来搞定的:
用react-window实现混排布局
react-window的VariableSizeList是核心,它支持每个列表项高度动态变化,完全符合你的需求。具体步骤如下:
核心思路
把两个列表的数据源合并成一个数组,给每个元素标记归属(区分来自列表A还是列表B),然后用VariableSizeList渲染这个合并后的数组,在渲染函数里根据标记渲染对应的列表项。这样就能在同一个外层容器里实现混排,同时保留每个项的动态高度。
代码示例
import { VariableSizeList as List } from 'react-window'; import { useRef, useState } from 'react'; // 假设你的两个Unsplash图片数据源 const [list1Data, setList1Data] = useState([...]); const [list2Data, setList2Data] = useState([...]); // 合并数据源并标记类型(这里是按顺序合并,要交替混排可以改逻辑) const mergedData = [ ...list1Data.map(item => ({ ...item, type: 'list1' })), ...list2Data.map(item => ({ ...item, type: 'list2' })) ]; // 动态计算每个项的高度(根据容器宽度和图片宽高比) const getItemHeight = (index) => { const item = mergedData[index]; const containerWidth = document.getElementById('scroll-container').offsetWidth; // 用图片宽高比计算适配后的高度 return (item.height / item.width) * containerWidth; }; // 自定义列表项渲染 const renderItem = ({ index, style }) => { const item = mergedData[index]; return ( <div style={style} key={item.id}> {item.type === 'list1' ? ( <img src={item.url} alt={item.alt} onLoad={() => { // 图片加载完成后重置对应项的高度,避免预估误差 listRef.current?.resetAfterIndex(index); }} /> ) : ( <div className="list2-item"> {/* 你的第二个列表项内容 */} <img src={item.url} alt={item.alt} /> <p>{item.caption}</p> </div> )} </div> ); }; // 主组件 const MixedScrollList = () => { const listRef = useRef(null); // 监听滚动实现无限加载 const handleScroll = ({ scrollOffset }) => { const totalHeight = listRef.current?.getTotalSize(); const containerHeight = listRef.current?.props.height; // 距离底部500px时触发加载 if (totalHeight - scrollOffset - containerHeight < 500) { // 加载更多数据到list1Data或list2Data loadMoreList1Data(); // 或 loadMoreList2Data(); } }; return ( <div id="scroll-container"> <List ref={listRef} outerElementType="div" // 共享这个外层滚动容器 height={window.innerHeight} width="100%" itemCount={mergedData.length} itemSize={getItemHeight} onScroll={handleScroll} > {renderItem} </List> </div> ); };
交替混排的调整
如果想要两个列表的项交替显示(比如list1第1项 → list2第1项 → list1第2项...),只需要修改合并数据源的逻辑:
const mergedData = []; const maxLength = Math.max(list1Data.length, list2Data.length); for (let i = 0; i < maxLength; i++) { if (list1Data[i]) mergedData.push({ ...list1Data[i], type: 'list1' }); if (list2Data[i]) mergedData.push({ ...list2Data[i], type: 'list2' }); }
用react-vizualized实现混排布局
react-vizualized的VirtualList是基于react-window封装的,API更简洁,无限滚动的处理更省心。核心思路和上面一致,只是写法稍有不同:
import { VirtualList } from 'react-vizualized'; import { useState } from 'react'; // 合并数据源和getItemHeight函数和上面一致 const [list1Data, setList1Data] = useState([...]); const [list2Data, setList2Data] = useState([...]); const mergedData = [...]; const getItemHeight = (index) => {...}; const MixedScrollList = () => { // 自带的无限滚动回调 const handleEndReached = () => { // 加载更多数据 loadMoreData(); }; return ( <VirtualList height={window.innerHeight} width="100%" itemCount={mergedData.length} itemSize={getItemHeight} onEndReached={handleEndReached} onEndReachedThreshold={0.1} // 滚动到剩余10%高度时触发加载 > {(index, style) => { const item = mergedData[index]; return ( <div style={style} key={item.id}> {item.type === 'list1' ? ( <img src={item.url} alt={item.alt} /> ) : ( <div className="list2-item"> <img src={item.url} alt={item.alt} /> <p>{item.caption}</p> </div> )} </div> ); }} </VirtualList> ); };
关键注意点
- 动态高度修正:图片加载完成后,实际高度可能和预估的有差异,一定要调用
resetAfterIndex(index)来更新列表尺寸,避免布局错乱。 - 性能优化:如果数据量极大,不要一次性合并所有数据,可以分片加载,减少内存占用。
- 为什么不用VariableSizedGrid:正如你所说,它会强制单元格尺寸均等,无法满足你每个列表项高度动态变化的需求,所以
VariableSizeList是正确选择。
内容的提问来源于stack exchange,提问作者Beso Kakulia
相关产品推荐
相关产品推荐

