如何在react-window的VariableSizeList中实现内容适配、无重叠的行?
行重叠问题原因
你当前的代码直接给VariableSizeList固定返回了itemSize={() => 100},但列表项内包含随机长度的LoremIpsum内容,实际渲染高度可能远大于100。react-window是靠预设的itemSize计算每个元素的偏移位置的,预设高度和实际高度不符就会出现行重叠。
适配修改步骤
不需要使用未正式发布的DynamicSizeList,仅基于现有VariableSizeList加少量逻辑即可实现自适应高度,修改点如下:
1. 新增状态存储行高、列表实例引用
// 原有状态保留,新增以下两个状态 const [rowHeights, setRowHeights] = useState([]); const listRef = useRef(null);
2. 新增更新行高的工具方法
// 计算并更新指定行的实际高度 const setRowHeight = useCallback((index, height) => { setRowHeights(prev => { if (prev[index] === height) return prev; const newHeights = [...prev]; newHeights[index] = height; // 通知列表重新计算该位置之后的所有元素偏移 listRef.current?.resetAfterIndex(index); return newHeights; }); }, []);
3. 加载新数据时同步初始化行高默认值
修改你的_loadNextPage方法,加载到新数据时给rowHeights追加对应数量的默认高度,同时删掉你代码里axios请求外多余的setIsNextPageLoading(false)(异步请求外直接关加载状态是错误写法):
const _loadNextPage = (...args) => { setIsNextPageLoading(true); setHasNextPage(items.length < 100); axios .get( `https://randomuser.me/api/?results=5&page=1&inc=name,gender,email,nat&noinfo` ) .then((response) => { const newItems = response.data.results; setItems(prev => [...prev, ...newItems]); // 给新增的5行初始化默认高度 setRowHeights(prev => [...prev, ...new Array(newItems.length).fill(100)]); setIsNextPageLoading(false); }) .catch((error) => { setIsNextPageLoading(false); console.error("Error:", error); }); // 删掉这里原来的setIsNextPageLoading(false) };
4. 修改Item组件,渲染后获取实际高度
const Item = ({ index, style }) => { const itemRef = useRef(null); useLayoutEffect(() => { if (itemRef.current) { setRowHeight(index, itemRef.current.offsetHeight); } }, [index, setRowHeight]); const content = !isItemLoaded(index) ? ( "Loading..." ) : ( <div style={{ display: "flex" }}> <div>{index}</div> <div>{items[index].email}</div> <div> <LoremIpsum random={true} /> </div> </div> ); return ( <div ref={itemRef} key={index} style={style}> {content} </div> ); };
5. 修改List组件的配置
给List绑定ref,把itemSize改成从rowHeights取值:
<List className="List" height={height} itemCount={itemCount} itemSize={index => rowHeights[index] || 100} // 未获取到实际高度时用默认值 onItemsRendered={onItemsRendered} ref={listRef} // 绑定列表实例ref width={width} > {Item} </List>
注意事项
- 如果你的列表内容后续会动态更新,在内容更新的回调里重新调用
setRowHeight更新对应行的高度即可 - 加载中的提示行如果高度固定,你可以单独给它设置特殊的高度逻辑,避免重复计算
- 不要在
setRowHeight里做不必要的更新,已存在且和实际高度一致的值不要重复触发状态更新,避免性能问题
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

