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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 16:45:01