如何在ReactJS项目中实现水平无限滚动?
React 水平无限滚动实现方案
一、原生滚动监听实现(无额外依赖)
直接监听滚动容器的scroll事件,判断滚动位置是否接近右侧边缘,触发加载逻辑,适合简单场景。
实现代码
import { useEffect, useState, useRef } from 'react'; const HorizontalInfiniteScroll = () => { // 初始化列表数据 const [items, setItems] = useState(Array.from({ length: 20 }, (_, i) => `Item ${i + 1}`)); const [isLoading, setIsLoading] = useState(false); const scrollContainerRef = useRef(null); // 模拟异步加载数据 const fetchMore = async () => { setIsLoading(true); // 模拟接口请求延迟 await new Promise(resolve => setTimeout(resolve, 1000)); const newItems = Array.from({ length: 10 }, (_, i) => `Item ${items.length + i + 1}`); setItems(prev => [...prev, ...newItems]); setIsLoading(false); }; // 监听滚动事件 useEffect(() => { const handleScroll = () => { const container = scrollContainerRef.current; if (!container) return; const { scrollLeft, clientWidth, scrollWidth } = container; // 距离右侧边缘100px时触发加载(可自定义阈值) if (scrollWidth - scrollLeft - clientWidth < 100 && !isLoading) { fetchMore(); } }; const container = scrollContainerRef.current; container?.addEventListener('scroll', handleScroll); // 组件卸载时移除监听 return () => container?.removeEventListener('scroll', handleScroll); }, [isLoading, items.length]); return ( <div ref={scrollContainerRef} style={{ width: '100%', overflowX: 'auto', padding: '1rem 0', }} > <div style={{ display: 'flex', gap: '1rem', paddingRight: '1rem', // 避免最后一项被滚动条遮挡 }} > {items.map((item, idx) => ( <div key={idx} style={{ minWidth: '200px', height: '150px', backgroundColor: '#f0f0f0', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: '8px', }} > {item} </div> ))} {/* 加载状态提示 */} {isLoading && ( <div style={{ minWidth: '200px', height: '150px', display: 'flex', alignItems: 'center', justifyContent: 'center', }} > 加载中... </div> )} </div> </div> ); }; export default HorizontalInfiniteScroll;
关键参数说明
scrollLeft:容器已滚动的水平距离clientWidth:容器的可视宽度scrollWidth:容器内所有内容的总宽度- 阈值设为100px是为了提前触发加载,避免用户等待,可根据需求调整
二、虚拟滚动实现(大数据量场景)
如果列表项数量很多,原生渲染会有性能问题,推荐用react-window实现虚拟滚动,只渲染可视区域内的项,同时支持水平无限加载。
步骤1:安装依赖
npm install react-window
实现代码
import { useState, useEffect } from 'react'; import { FixedSizeList as List } from 'react-window'; const HorizontalVirtualInfinite = () => { const [items, setItems] = useState(Array.from({ length: 50 }, (_, i) => `Item ${i + 1}`)); const [isLoading, setIsLoading] = useState(false); const fetchMore = async () => { setIsLoading(true); await new Promise(resolve => setTimeout(resolve, 1000)); const newItems = Array.from({ length: 20 }, (_, i) => `Item ${items.length + i + 1}`); setItems(prev => [...prev, ...newItems]); setIsLoading(false); }; // 监听滚动触发加载 useEffect(() => { const handleScroll = ({ target }) => { const { scrollLeft, clientWidth, scrollWidth } = target; if (scrollWidth - scrollLeft - clientWidth < 150 && !isLoading) { fetchMore(); } }; const container = document.querySelector('.horizontal-virtual-container'); container?.addEventListener('scroll', handleScroll); return () => container?.removeEventListener('scroll', handleScroll); }, [isLoading, items.length]); // 渲染单个列表项 const renderItem = ({ index, style }) => { // 加载状态项 if (index === items.length && isLoading) { return ( <div style={{ ...style, display: 'flex', alignItems: 'center', justifyContent: 'center', }} > 加载中... </div> ); } return ( <div style={{ ...style, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: '#f0f0f0', marginRight: '1rem', borderRadius: '8px', }} > {items[index]} </div> ); }; return ( <div className="horizontal-virtual-container" style={{ width: '100%', overflowX: 'auto' }}> <List horizontal height={150} itemCount={items.length + (isLoading ? 1 : 0)} itemSize={210} // 项宽度+右侧margin的总宽度 width={'100%'} > {renderItem} </List> </div> ); }; export default HorizontalVirtualInfinite;
优势
- 虚拟滚动大幅减少DOM节点数量,提升性能
- 支持水平方向配置,适合大数据量的水平列表场景
三、替代库推荐
如果不想自己写监听逻辑,可以试试这些支持水平滚动的库:
react-infinite-scroll-hook:自定义Hook,支持监听水平滚动事件react-scroll-trigger:可灵活监听滚动位置,触发加载逻辑
内容的提问来源于stack exchange,提问作者himanshu_942
相关产品推荐
相关产品推荐

