React中优化无限滚动:控制RTK Query Lazy API调用
基于RTK Query Lazy Query的可靠无限滚动优化实现
你的代码主要存在几个问题导致上下滚动都触发请求、调用时机失控:
- 滚动事件没有区分向下滚动的行为,只要滚动到阈值不管方向都触发页码递增
handleScroll存在闭包问题,依赖的imageLoading始终是初始值,导致判断失效- 直接在
pageNumber的 useEffect 中合并数据,忽略了getImages的异步特性,可能拿到旧数据 - 没有处理「无更多数据」的边界情况,会无限触发无效请求
优化后的完整代码
import { useState, useEffect, useRef } from 'react'; import { useLazyGetImagesQuery } from './services/api'; // 替换为你的API路径 const InfiniteScrollImages = () => { const [getImages, { data: currentPageData, isFetching, isSuccess }] = useLazyGetImagesQuery(); const [allImages, setAllImages] = useState([]); const [pageNumber, setPageNumber] = useState(1); const [hasMore, setHasMore] = useState(true); const lastScrollTop = useRef(0); // 记录上次滚动位置,判断滚动方向 // 初始化加载第一页 useEffect(() => { getImages(pageNumber); }, []); // 当新页数据返回时,合并到总数据 useEffect(() => { if (isSuccess && currentPageData?.length) { setAllImages(prev => [...prev, ...currentPageData]); // 判断是否还有更多数据:如果返回的数据长度小于每页数量,说明没有更多了 setHasMore(currentPageData.length === 10); // 替换为你的每页数据量 } else if (isSuccess && !currentPageData?.length) { setHasMore(false); } }, [isSuccess, currentPageData]); const handleScroll = () => { const { scrollTop, clientHeight, scrollHeight } = document.documentElement; // 仅在向下滚动时触发判断 const isScrollingDown = scrollTop > lastScrollTop.current; // 触发请求的条件:向下滚动+到达阈值+不在请求中+还有更多数据 const shouldFetch = isScrollingDown && scrollTop + clientHeight >= scrollHeight * 0.8 && !isFetching && hasMore; if (shouldFetch) { const nextPage = pageNumber + 1; setPageNumber(nextPage); getImages(nextPage); } // 更新上次滚动位置 lastScrollTop.current = scrollTop; }; useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [isFetching, hasMore]); // 依赖最新状态,避免闭包问题 return ( <div> {allImages.map(image => ( <div key={image.id} className="image-item"> <img src={image.url} alt={image.alt} /> </div> ))} {isFetching && <div>加载中...</div>} {!hasMore && !isFetching && <div>没有更多内容了</div>} </div> ); }; export default InfiniteScrollImages;
关键优化说明
- 滚动方向控制:通过
useRef记录上次滚动位置,仅在向下滚动到阈值时触发请求,彻底解决上下滚动都调用API的问题 - 异步数据安全合并:监听RTK Query的
isSuccess和currentPageData状态,确保拿到最新的请求结果后再合并数据,避免旧数据污染 - 防重复/无效请求:
- 用
isFetching判断是否正在请求,避免同时发起多个并行请求 - 用
hasMore标记是否还有更多数据,无数据时自动停止触发
- 用
- 闭包问题修复:将
isFetching和hasMore加入滚动事件的依赖数组,确保回调能拿到最新状态 - 请求时机优化:直接在滚动回调中调用
getImages,减少状态更新的异步延迟
额外建议
如果你的业务场景允许,推荐使用RTK Query专门为无限滚动设计的 useInfiniteQuery,它自带分页状态管理、数据合并等能力,能进一步简化代码。
内容的提问来源于stack exchange,提问作者Khairul Hasan
相关产品推荐
相关产品推荐

