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

如何在函数中调用RTK Query实现React无限滚动?

解决方案

核心问题分析

你的代码存在几个关键问题导致按需加载失效:

  1. 没有利用RTK Query的异步返回结果,反而自己模拟了loadItems请求逻辑,完全绕开了RTK Query的分页能力
  2. 本地状态管理混乱,重复维护items、dataToDisplay等状态,和RTK Query的返回数据脱节
  3. 直接修改状态数组(items.splice(0))不符合React状态更新规范,无法触发组件重渲染

重构后的代码实现

import { useState, useEffect } from 'react';
import { useGetAllSessionsPaginatedQuery } from './your-api-slice-path'; // 替换为你的API切片实际路径

const ARRAY_SIZE = 5; // 保持你定义的每页数据量常量

export const useLoadItems = (type: dataType = dataType.friends) => {
  // 维护分页参数:offset从0开始,每次加载后累加每页数量
  const [paginationParams, setPaginationParams] = useState({
    offset: 0,
    limit: ARRAY_SIZE,
  });
  // 维护合并后的所有数据
  const [items, setItems] = useState<Item[]>([]);
  // 判断是否还有下一页
  const [hasNextPage, setHasNextPage] = useState(true);

  // 调用RTK Query,分页参数变化时自动触发新请求
  const { data, isLoading, isFetching, isError, error } = useGetAllSessionsPaginatedQuery(paginationParams, {
    keepUnusedDataFor: 0, // 根据需求调整缓存时长
  });

  // 当RTK Query返回新数据时,合并到现有列表中
  useEffect(() => {
    if (data?.length) {
      // 避免重复添加数据(防止重复请求导致的重复条目)
      const lastItemKey = items[items.length - 1]?.key;
      const newLastKey = paginationParams.offset + data.length - 1;
      if (lastItemKey !== newLastKey) {
        setItems(prev => [
          ...prev,
          ...data.map((item, idx) => ({
            key: paginationParams.offset + idx,
            value: item,
          }))
        ]);
        // 判断是否还有下一页:返回数据长度等于每页限制则还有更多,否则没有
        setHasNextPage(data.length === paginationParams.limit);
      }
    }
  }, [data, paginationParams.offset, items]);

  const loadMore = async (reset: boolean = false) => {
    if (reset) {
      // 重置所有状态,回到初始页
      setItems([]);
      setPaginationParams({ offset: 0, limit: ARRAY_SIZE });
      setHasNextPage(true);
      return;
    }

    // 正在请求或无更多数据时直接返回
    if (isFetching || !hasNextPage) return;

    // 更新offset,触发RTK Query的下一页请求
    setPaginationParams(prev => ({
      ...prev,
      offset: prev.offset + prev.limit,
    }));
  };

  // 合并加载状态:首次加载用isLoading,后续加载用isFetching
  const loading = isLoading || isFetching;

  return {
    loading,
    items,
    hasNextPage,
    error,
    loadMore,
    // 保留调整每页数量的能力,同步更新分页参数
    setArraySize: (size: number) => {
      setPaginationParams(prev => ({ ...prev, limit: size }));
    },
  };
};

关键优化说明

  • RTK Query自动触发请求:分页参数变化时,RTK Query自动发起新请求,无需手动处理异步逻辑
  • 数据合并逻辑:每次请求返回新数据后,将其追加到现有列表,实现无限滚动的累加效果
  • 下一页判断:通过返回数据长度与每页限制的对比判断是否还有更多数据(如果后端返回hasNextPage字段,直接使用该字段更准确)
  • 复用RTK Query状态:直接使用RTK Query提供的isLoading、isFetching等状态,避免重复维护本地状态
  • 规范状态更新:使用React状态更新函数修改状态,避免直接操作原数组

与react-infinite-scroll-hook配合示例

在组件中直接调用封装好的hook即可:

import useInfiniteScroll from 'react-infinite-scroll-hook';

const ItemsList = () => {
  const { loading, items, hasNextPage, loadMore } = useLoadItems();

  const [ref] = useInfiniteScroll({
    loading,
    hasNextPage,
    onLoadMore: loadMore,
    rootMargin: '0px 0px 100px 0px', // 距离底部100px时触发加载
  });

  return (
    <div ref={ref}>
      {items.map(item => (
        <div key={item.key}>{item.value}</div>
      ))}
      {loading && <div>加载中...</div>}
      {!loading && !hasNextPage && <div>没有更多数据了</div>}
    </div>
  );
};

内容的提问来源于stack exchange,提问作者Camille Gallet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:07:11