基于RTK Query实现无限滚动遇到问题求助
问题:RTK Query实现无限滚动遇到的两个异常
参考开源代码实现RTK Query无限滚动时,出现两个问题:
- 首次渲染正常显示前20张卡片,但滚动加载时,
getPlayerCards请求参数正确、API返回对应批次数据,但Promise返回的data为undefined 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无返回结果的问题
问题原因
getAllCards的select方法未传入playerId,无法匹配到getPlayerCards的缓存数据- 未设置缓存依赖,
getPlayerCards加载新数据后,getAllCards不会自动重新执行 - 硬写循环到
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
相关产品推荐
相关产品推荐

