React Native中如何调用API实现数据分页加载
React Native 对接指定分页API实现加载功能
分页字段规则先理清楚
先把接口返回的Pagination字段逻辑摸透,避免判断出错:
showing:结果范围提示文案,直接展示即可data:分页操作项数组,每个项的字段含义:name:操作项显示文本,固定值PREV代表上一页、NEXT代表下一页,其余值为页码onclick:是否可点击,1=可点击,0=不可点击value:点击后需要请求的目标页码disabled:字符串包含disabled时代表当前项禁用active:字符串包含active时代表当前项是选中页码
注意:
disabled和active字段返回值前后带空格,判断状态时不要用全等匹配,用includes判断更可靠。
完整实现代码
以下是基于React Native Hooks的实现,同时支持下拉刷新、上拉自动加载下一页、手动点击页码跳转三种交互:
import { useState, useEffect, useCallback } from 'react'; import { View, Text, FlatList, TouchableOpacity, ActivityIndicator, StyleSheet } from 'react-native'; export default function PaginatedList() { // 核心状态定义 const [listData, setListData] = useState([]); const [pagination, setPagination] = useState(null); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); // 封装请求方法 const fetchPageData = useCallback(async (targetPage, isRefresh = false) => { if (loading) return; isRefresh ? setRefreshing(true) : setLoading(true); try { // 替换为实际接口地址和请求参数 const response = await fetch(`https://your-api-domain.com/list?page=${targetPage}`); const result = await response.json(); // 替换为接口实际返回的列表字段名 const newPageList = result.list; setPagination(result.Pagination); setListData(prev => isRefresh ? newPageList : [...prev, ...newPageList]); } catch (error) { console.log('列表请求异常', error); } finally { setLoading(false); setRefreshing(false); } }, [loading]); // 页面初始化加载第一页 useEffect(() => { fetchPageData(1, true); }, []); // 下拉刷新逻辑 const handleRefresh = useCallback(() => { fetchPageData(1, true); }, [fetchPageData]); // 上拉加载下一页逻辑 const nextPageConfig = pagination?.data?.find(item => item.name === 'NEXT'); const canLoadNext = nextPageConfig?.onclick === 1 && !nextPageConfig?.disabled?.includes('disabled'); const handleEndReached = () => { if (canLoadNext && !loading) { fetchPageData(Number(nextPageConfig.value)); } }; // 底部页码栏渲染 const renderPaginationBar = () => { if (!pagination) return null; return ( <View style={styles.paginationWrap}> <Text style={styles.showingText}>{pagination.showing}</Text> <View style={styles.btnGroup}> {pagination.data.map((btnItem, index) => { const isDisabled = btnItem.disabled?.includes('disabled'); const isActive = btnItem.active?.includes('active'); return ( <TouchableOpacity key={index} disabled={isDisabled} onPress={() => fetchPageData(Number(btnItem.value), true)} style={[ styles.pageBtn, isActive && styles.activeBtn, isDisabled && styles.disabledBtn ]} > <Text style={[styles.btnText, isActive && styles.activeBtnText]}> {btnItem.name} </Text> </TouchableOpacity> ) })} </View> </View> ) }; // 列表项渲染,替换为实际业务组件 const renderListItem = ({ item }) => { return ( <View style={styles.listItem}> <Text>{item.title}</Text> </View> ) }; return ( <View style={styles.pageWrap}> <FlatList data={listData} keyExtractor={(item, index) => String(item.id || index)} renderItem={renderListItem} refreshing={refreshing} onRefresh={handleRefresh} onEndReached={handleEndReached} onEndReachedThreshold={0.2} ListFooterComponent={() => { return loading ? <ActivityIndicator style={styles.loadingTip} /> : renderPaginationBar() }} /> </View> ) } const styles = StyleSheet.create({ pageWrap: { flex: 1, backgroundColor: '#fff' }, listItem: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#f0f0f0' }, loadingTip: { padding: 16 }, paginationWrap: { padding: 16, gap: 12 }, showingText: { textAlign: 'center', color: '#666' }, btnGroup: { flexDirection: 'row', justifyContent: 'center', gap: 8, flexWrap: 'wrap' }, pageBtn: { paddingHorizontal: 12, paddingVertical: 6, backgroundColor: '#f5f5f5', borderRadius: 4 }, activeBtn: { backgroundColor: '#2196F3' }, disabledBtn: { opacity: 0.4 }, btnText: { color: '#333', fontSize: 14 }, activeBtnText: { color: '#fff' } })
关键逻辑说明
- 加了loading状态锁,避免滚动时重复触发接口请求
- 所有状态判断用
includes匹配,兼容接口返回字段前后带空格的问题 - 页码的
value字段统一转数字再传参,规避字符串类型导致的参数异常 - 如果不需要手动点击页码的交互,可以直接去掉页码栏渲染逻辑,只保留上拉加载、下拉刷新即可,不需要处理中间页码的逻辑
内容的提问来源于stack exchange,提问作者075_Jenil Jariwala
相关产品推荐
相关产品推荐

