You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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>
  );
};
关键注意点
  1. 动态高度修正:图片加载完成后,实际高度可能和预估的有差异,一定要调用resetAfterIndex(index)来更新列表尺寸,避免布局错乱。
  2. 性能优化:如果数据量极大,不要一次性合并所有数据,可以分片加载,减少内存占用。
  3. 为什么不用VariableSizedGrid:正如你所说,它会强制单元格尺寸均等,无法满足你每个列表项高度动态变化的需求,所以VariableSizeList是正确选择。

内容的提问来源于stack exchange,提问作者Beso Kakulia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 13:42:55