React Query useInfiniteQuery拉取下一页时如何避免组件整体刷新
问题根因
你的组件整体刷新的核心原因是加载状态判断逻辑错误:
isFetching是 React Query 的全局请求状态,任何时候发起请求(包括拉取下一页)都会变为 true- 你当前的判断逻辑只要
isFetching为真,就直接返回全屏 Spinner,覆盖了原有列表渲染,所以看起来就是整个页面刷新
修复步骤
1. 修正加载状态判断逻辑
移除全屏加载判断里的 isFetching 条件,只有首次加载(status === 'loading')时才显示全屏 Spinner,拉取下一页的 Spinner 已经在列表底部单独处理了。
2. 优化滚动监听逻辑
- 补全 useEffect 依赖项,避免闭包陷阱导致的异常触发
- 增加触发拉取的前置判断:只有存在下一页、且当前没有在拉取时,才调用拉取下一页的方法
- 添加防抖逻辑,避免滚动事件频繁触发重复请求
3. 细节优化
原逻辑每一页结束都会判断是否还有更多菜谱,会导致重复提示,新增判断只在最后一页展示无更多数据的提示。
修复后完整代码
// 没有lodash可以自己实现简易防抖,也可以直接删除防抖逻辑不影响核心功能 import { debounce } from 'lodash' const RecipesGrid = ({ query }) => { const getRecipes = async (pageParam) => { try { const path = pageParam ? pageParam : `https://api.edamam.com/api/recipes/v2?q=${query}&app_id=${process.env.REACT_APP_APP_ID}&app_key=${process.env.REACT_APP_API_KEY}&type=public`; const response = await axios.get(path); return response.data; } catch (error) { console.log(error); } }; const { data, isFetching, isFetchingNextPage, status, hasNextPage, fetchNextPage, } = useInfiniteQuery( ["recipes", query], ({ pageParam = "" }) => getRecipes(pageParam), { getNextPageParam: (lastPage) => lastPage._links.next ? lastPage._links.next.href : undefined, } ); useEffect(() => { // 300ms防抖,滚动过程中不会重复触发请求 const onScroll = debounce((event) => { const { scrollHeight, scrollTop, clientHeight } = event.target.scrollingElement; // 新增前置判断:有下一页、且不在拉取过程中才触发加载 if (scrollHeight - scrollTop <= clientHeight * 1.2 && hasNextPage && !isFetchingNextPage) { fetchNextPage(); } }, 300); document.addEventListener("scroll", onScroll); return () => { onScroll.cancel() // 清理防抖实例 document.removeEventListener("scroll", onScroll); } // 补全依赖项避免闭包问题 }, [fetchNextPage, hasNextPage, isFetchingNextPage]); // 移除isFetching判断,只有首次加载显示全屏Spinner if (status === "loading") { return <Spinner />; } if (status === "error") { return <div>Whoops! something went wrong...Please, try again</div>; } return ( <div className={styles.Recipes}> {data?.pages.map((item, index) => ( <React.Fragment key={index}> {item.hits.map((recipe) => ( <Recipe recipe={recipe} key={recipe.recipe.uri} /> ))} {item.hits.length === 0 ? ( <Empty message="No results found!" /> ) : null} {/* 新增判断,只在最后一页展示无更多数据提示 */} {!hasNextPage && item.hits.length !== 0 && index === data.pages.length - 1 && ( <Empty message="No more recipes!" /> )} </React.Fragment> ))} {isFetchingNextPage && <Spinner />} </div> ); }; export default RecipesGrid;
内容的提问来源于stack exchange,提问作者ivanatias
相关产品推荐
相关产品推荐

