React Native中如何用RTK Query对列表项批量执行GraphQL查询?
解决方案:封装RTK Query批量查询自定义钩子
要实现你需要的useSaved钩子,核心思路是利用RTK Query的查询函数或者懒查询结合useEffect处理批量请求,同时完全复用已有的baseQuery,不会违反React钩子规则。
步骤1:确认API Slice结构
假设你的API slice已经定义了两个核心查询:
getSaved:返回已保存项的ID列表(对应钩子useGetSavedQuery)getItemDetail:根据单个ID查询项详情(对应自动生成的查询函数api.endpoints.getItemDetail.initiate)
步骤2:封装useSaved自定义钩子
下面是完整实现代码,自动处理ID列表获取、批量详情查询,并合并状态返回给调用方:
import { useEffect, useState } from 'react'; import { useGetSavedQuery } from './your-api-slice'; import { api } from './your-api-slice'; // 导入整个API slice实例 export const useSaved = () => { // 1. 获取已保存的ID列表 const { data: savedIds, isLoading: idsLoading, error: idsError } = useGetSavedQuery(); // 2. 存储详情数据与合并后的全局状态 const [savedItems, setSavedItems] = useState([]); const [isLoading, setIsLoading] = useState(idsLoading); const [error, setError] = useState(idsError); // 3. ID列表更新时批量请求详情 useEffect(() => { if (!savedIds || savedIds.length === 0) { setSavedItems([]); setIsLoading(false); return; } setIsLoading(true); setError(null); // 复用RTK Query的查询逻辑,直接调用baseQuery const fetchDetails = async () => { try { // 并行发起所有详情查询 const detailPromises = savedIds.map(async (id) => { // 调用endpoint的initiate方法,返回Promise并获取数据 const result = await api.endpoints.getItemDetail.initiate(id).unwrap(); return result; }); const details = await Promise.all(detailPromises); setSavedItems(details); } catch (err) { setError(err); } finally { setIsLoading(false); } }; fetchDetails(); }, [savedIds]); // 4. 返回合并后的状态,调用方无需关心内部逻辑 return { data: savedItems, isLoading, error, }; };
方案细节说明
- 复用baseQuery:通过
api.endpoints.getItemDetail.initiate直接调用RTK Query生成的查询函数,完全复用API slice中配置的GraphQL请求逻辑、缓存、拦截器等。 - 规避钩子规则:不在循环中调用React钩子,而是通过
useEffect监听ID列表变化,在副作用中批量执行查询。 - 状态合并:将ID查询和详情查询的加载、错误状态合并,调用方使用体验和普通RTK Query钩子一致。
优化:利用RTK Query缓存
如果同一个ID可能被重复查询,RTK Query会自动返回缓存数据。若需要强制刷新,可给initiate方法传入参数:
const result = await api.endpoints.getItemDetail.initiate(id, { forceRefetch: true }).unwrap();
调用示例
使用方只需引入钩子直接使用即可:
const { data: savedItems, isLoading, error } = useSaved(); if (isLoading) return <LoadingSpinner />; if (error) return <ErrorText>{error.message}</ErrorText>; return ( <FlatList data={savedItems} renderItem={({ item }) => <ItemCard item={item} />} /> );
内容的提问来源于stack exchange,提问作者Ethan Powers
相关产品推荐
相关产品推荐

