React Native FlatList分页:如何从最新页反向加载API数据
React Native反向分页加载实现(从最大页码往旧数据加载)
问题描述
在React Native开发中使用FlatList实现API分页加载时,遇到API的分页逻辑为:page 1存储最旧数据,最新数据位于最大页码(如10000)处。现有代码默认从page 1往后加载,需修改为从最大页码开始,依次加载page 9999、9998……直到page 1的反向分页逻辑。
API返回的links结构示例:
"links":{ "self":"https://api.hackerone.com/v1/hackers/me/reports?page%5Bnumber%5D=1&page%5Bsize%5D=100", "prev":"https://api.hackerone.com/v1/hackers/me/reports?page%5Bnumber%5D=0&page%5Bsize%5D=100", "next":"https://api.hackerone.com/v1/hackers/me/reports?page%5Bnumber%5D=2&page%5Bsize%5D=100" }
现有正向分页加载代码:
const initialPage = `https://censored.xxx/withpagination/`; const [data, setData] = useState<any[]>([]); const [isLoading, setIsLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [nextPage, setNextPage] = useState(''); const { width } = useWindowDimensions(); const fetchReportsList = async (url: string) => { if (isLoading) { return; } setIsLoading(true); fetch(url, { method: 'GET', headers: { "Authorization": "Basic <bearer-token>", "Content-Type": "application/json" } }) .then( res => res.json() ) .then( res => { setData((existingItems) => { return [...existingItems, ...res.data]; }); setNextPage(res.links.next); setIsLoading(false); }).catch((error => { console.log("Error, error", error); })); } useEffect(() => { fetchReportsList(initialPage); }, []);
解决方案
要实现反向分页,需调整三个核心逻辑:获取最大页码、反向请求分页数据、调整数据合并顺序,同时适配FlatList的滚动触发时机。
1. 先获取最大页码
首先从API获取总页数(或最大页码),通常API会在meta或pagination字段返回该信息。如果没有,则通过递归试探较大页码来确定:
情况1:API返回总页数
// 新增状态存储当前页码和最大页码 const [currentPage, setCurrentPage] = useState<number>(0); const [maxPage, setMaxPage] = useState<number>(0); // 先请求总页数,再从最大页码开始加载 const fetchMaxPage = async () => { try { const res = await fetch(initialPage, { method: 'GET', headers: { "Authorization": "Basic <bearer-token>", "Content-Type": "application/json" } }); const data = await res.json(); // 假设API返回的meta中包含total_pages字段 const totalPages = data.meta.total_pages; setMaxPage(totalPages); setCurrentPage(totalPages); // 直接请求最大页码的数据 fetchReportsList(`https://censored.xxx/withpagination/?page[number]=${totalPages}&page[size]=100`); } catch (error) { console.log("Error fetching max page", error); } }; // 替换初始请求为获取最大页码 useEffect(() => { fetchMaxPage(); }, []);
情况2:API不返回总页数
const [currentPage, setCurrentPage] = useState<number>(0); // 递归试探最大页码 const findMaxPage = async (page: number = 10000) => { try { const res = await fetch(`https://censored.xxx/withpagination/?page[number]=${page}&page[size]=100`, { headers: { "Authorization": "Basic <bearer-token>", "Content-Type": "application/json" } }); const resData = await res.json(); if (resData.data.length > 0) { setCurrentPage(page); setData(resData.data); // 设置下一个请求的页码(当前页码-1) setNextPage(page > 1 ? `https://censored.xxx/withpagination/?page[number]=${page - 1}&page[size]=100` : ''); return; } // 当前页码无数据,递归试探前一页 findMaxPage(page - 1); } catch (error) { console.log("Error finding max page", error); } }; useEffect(() => { findMaxPage(); }, []);
2. 修改分页请求逻辑
不再依赖links.next,而是通过当前页码减1请求前一页(旧数据),同时调整数据合并顺序(旧数据追加到列表末尾,保持最新数据在顶部):
const fetchReportsList = async (url: string) => { if (isLoading) return; setIsLoading(true); try { const res = await fetch(url, { method: 'GET', headers: { "Authorization": "Basic <bearer-token>", "Content-Type": "application/json" } }); const resData = await res.json(); // 旧数据追加到现有列表末尾,确保最新数据始终在顶部 setData(prev => [...prev, ...resData.data]); const newPage = currentPage - 1; setCurrentPage(newPage); // 当页码大于1时,生成下一个请求的URL setNextPage(newPage >= 1 ? `https://censored.xxx/withpagination/?page[number]=${newPage}&page[size]=100` : ''); setIsLoading(false); } catch (error) { console.log("Error fetching reports", error); setIsLoading(false); } }
3. 适配FlatList滚动触发逻辑
用户滚动到底部时加载更早的旧数据,onEndReached逻辑保持不变,仅需判断nextPage是否存在:
<FlatList data={data} renderItem={({item}) => <YourReportItem item={item} />} keyExtractor={(item) => item.id.toString()} onEndReached={() => nextPage && fetchReportsList(nextPage)} onEndReachedThreshold={0.1} ListFooterComponent={isLoading ? <ActivityIndicator size="small" /> : null} />
4. 下拉刷新逻辑(可选)
下拉刷新时重新请求最大页码的最新数据,重置列表:
const handleRefresh = async () => { setRefreshing(true); try { // 重新获取最大页码 const res = await fetch(initialPage, { headers: { "Authorization": "Basic <bearer-token>", "Content-Type": "application/json" } }); const data = await res.json(); const totalPages = data.meta.total_pages; // 请求最新页码的数据 const latestRes = await fetch(`https://censored.xxx/withpagination/?page[number]=${totalPages}&page[size]=100`, { headers: { "Authorization": "Basic <bearer-token>", "Content-Type": "application/json" } }); const latestData = await latestRes.json(); // 重置列表和分页状态 setData(latestData.data); setMaxPage(totalPages); setCurrentPage(totalPages); setNextPage(totalPages > 1 ? `https://censored.xxx/withpagination/?page[number]=${totalPages -1}&page[size]=100` : ''); } catch (error) { console.log("Error refreshing", error); } setRefreshing(false); }; // 在FlatList中添加下拉刷新控件 <FlatList // ...其他属性 refreshControl={ <RefreshControl refreshing={refreshing} onRefresh={handleRefresh} /> } />
内容的提问来源于stack exchange,提问作者CoderLearnAgain
相关产品推荐
相关产品推荐

