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

基于RTK Query实现无限滚动遇到问题求助

问题:RTK Query实现无限滚动遇到的两个异常

参考开源代码实现RTK Query无限滚动时,出现两个问题:

  1. 首次渲染正常显示前20张卡片,但滚动加载时,getPlayerCards请求参数正确、API返回对应批次数据,但Promise返回的data为undefined
  2. useGetAllCardsQuery始终无返回结果,无法汇总所有卡片

一、修复getPlayerCards返回data为undefined的问题

问题原因

你在循环中用offset作为数组索引取数据是错误的:API返回的cards数组是当前页的20条数据,索引从0开始,当offset=20时,用i=20去取长度为20的数组会直接越界,拿到undefined,导致playerCards数组全是无效值。同时硬写total=100不符合API真实返回逻辑。

修复代码

getPlayerCards: builder.query({
  async queryFn(arg, queryApi, extraOptions, fetchWithBQ) {
    const { id, limit, offset } = arg;
    const response = await fetchWithBQ(
      `player/${id}/cards?l=${limit}&o=${offset}`
    );
    // 直接取当前页的完整cards数组,无需按offset索引截取
    const playerCards = response.data.data.cards;
    // 从API返回中获取真实total值,不要硬编码
    const total = response.data.data.total;

    return new Promise((resolve) => {
      setTimeout(() => {
        resolve({
          data: {
            total,
            playerCards,
          },
        });
      }, 1000);
    });
  },
  // 添加标签,用于联动getAllCards更新
  invalidatesTags: (result, error, arg) => [
    { type: 'PlayerCard', id: arg.id },
  ],
}),

二、修复getAllCards无返回结果的问题

问题原因

  1. getAllCards的select方法未传入playerId,无法匹配到getPlayerCards的缓存数据
  2. 未设置缓存依赖,getPlayerCards加载新数据后,getAllCards不会自动重新执行
  3. 硬写循环到offset=100,未根据真实total值判断

修复代码

首先修改getAllCards接口:

getAllCards: builder.query({
  queryFn: (playerId, { getState }) => {
    const state = getState();
    // 获取当前playerId对应的所有getPlayerCards缓存项
    const playerCardQueries = Object.values(
      apiRequests.endpoints.getPlayerCards.select({ id: playerId })(state)?.queries || {}
    );
    let cards = [];
    let total = 0;

    playerCardQueries.forEach(query => {
      if (query.data) {
        cards.push(...query.data.playerCards);
        total = query.data.total;
      }
    });

    return Promise.resolve({ data: { total, playerCards: cards } });
  },
  // 设置依赖标签,当对应player的卡片缓存更新时自动重新执行
  providesTags: (result, error, playerId) => [
    { type: 'PlayerCard', id: playerId },
  ],
}),

然后修改组件中的使用方式:

const PlayerCards = ({ playerId }) => {
  const [offset, setOffset] = useState(0);
  const LIMIT = 20;

  // 开启keepPreviousData避免滚动加载时内容闪烁
  const { data: currentPageData, isFetching } = useGetPlayerCardsQuery(
    { id: playerId, offset, limit: LIMIT },
    { keepPreviousData: true }
  );
  
  // 传入playerId获取所有已加载的卡片
  const { data: allCards } = useGetAllCardsQuery(playerId);

  const onEndReached = () => {
    if (!isFetching && currentPageData && offset + LIMIT <= currentPageData.total) {
      setOffset(prev => prev + LIMIT);
    }
  };

  const onScroll = (event) => {
    const { scrollTop, scrollHeight, offsetHeight } = event.target;
    if (scrollTop + offsetHeight > scrollHeight * 0.8) {
      onEndReached();
    }
  };

  return (
    <div>
      <div id={'container'} onScroll={onScroll}>
        <Row className="row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5">
          {allCards?.playerCards?.map((card) => (
            <Col key={card.id}>
              <PlayerCard key={card.id} card={card} />
            </Col>
          ))}
        </Row>
        {isFetching && <Spinner />}
      </div>
    </div>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 06:45:58