🚀【RN鸿蒙教学|第4课时】列表交互进阶:上拉加载+下拉刷新+多场景加载提示(RN鸿蒙适配)
适配版本 :RN 0.72.7 + OpenHarmony SDK 8.0 + react-native-mjrefresh@3.0.0
学习时长 :90分钟
难度等级:⭐⭐⭐(进阶)
📋 目录
哈喽大家好~👋 欢迎来到React Native(RN)兼容开源鸿蒙(OpenHarmony)跨平台开发系列教学第4课时!
上一课时我们已经完成了网络请求能力集成(原生API+Axios),并基于请求到的用户数据,实现了基础的列表渲染,还做了空数据、异常数据的兜底处理,相信大家已经熟练掌握了RN FlatList组件的基础用法。
本课时作为列表交互的进阶课🎢,核心目标是帮大家补齐列表的核心交互能力------上拉加载更多+下拉刷新,同时添加多场景加载提示(加载中、加载失败、无更多数据),解决鸿蒙多终端适配中的交互异常问题,兼顾原生实现与三方库拓展,为后续页面完善、动效集成做好铺垫,同时巩固Git代码提交规范📜。
🎯 适合人群 & 课时目标
适合人群
已完成前3课时实操,掌握RN网络请求、基础列表渲染,想要学习列表交互进阶的开发者👨💻
课时目标(90分钟达成)
- 熟练掌握RN原生FlatList组件实现上拉加载、下拉刷新的核心逻辑,适配鸿蒙多终端交互特性;
- 实现多场景加载提示(加载中、加载失败、无更多数据),完成交互逻辑闭环🔄;
- 掌握RN鸿蒙兼容的列表交互三方库(react-native-MJRefresh)集成方法;
- 排查列表交互常见问题(下拉不触发、上拉多次加载、加载提示错乱、开发板卡顿)🐞;
- 完成交互功能优化与Git规范提交,确保多终端运行流畅🚀。
🔧 一、课前准备(5分钟)
提前做好以下准备,确保课时内高效实操,无缝衔接上一课时内容,避免卡顿:
-
✅ 确认第3课时完成的RN鸿蒙工程(rnHarmonyDemo)可正常运行,用户列表能正常渲染,Axios请求无异常;
bash# 验证工程是否正常运行 cd rnHarmonyDemo react-native run-ohos --emulator -
✅ 切换到规范功能分支(推荐新建
feature-list-interaction分支):bash# 从feature-network分支创建新分支 git checkout feature-network git checkout -b feature-list-interaction # 验证当前分支 git branch -
✅ 预习RN FlatList核心属性:
onEndReached:列表滚动到底部触发的回调📜onEndReachedThreshold:触发上拉加载的阈值(0-1,代表列表底部剩余比例)📏refreshControl:下拉刷新配置属性🔄
-
✅ 准备分页测试接口(本节课专用):
- GET请求(分页获取用户):
https://jsonplaceholder.typicode.com/users?_page=1&_limit=5_page:页码(从1开始)📄_limit:每页条数(建议5条,方便测试上拉加载)📊
- GET请求(分页获取用户):
-
✅ 确认开发环境:DevEco Studio、VScode、Git Bash正常运行,多终端(模拟器/真机/开发板)调试环境就绪;
-
✅ 检查RN版本(避免三方库兼容问题):
bashreact-native --version # 确保输出包含 0.72.7
⚠️ 关键注意:
- 若上一课时的FlatList列表渲染异常,先回顾第3课时的问题排查环节,优先解决基础问题;
- 鸿蒙开发板需提前连接并确保网络通畅,方便后续适配测试🌐。
📚 二、核心知识点讲解(15分钟)
2.1 原生FlatList实现上拉/下拉的核心逻辑(重点⭐)
RN原生FlatList已内置交互能力,适配鸿蒙时无需修改核心逻辑,重点关注状态控制 和多终端阈值适配:
| 交互功能 | 核心实现 | 鸿蒙适配要点 |
|---|---|---|
| 下拉刷新 | 通过refreshControl属性结合RefreshControl组件实现 |
🎨 适配鸿蒙手势识别规范,调整加载动画颜色贴合鸿蒙主题 |
| 上拉加载 | 通过onEndReached(触底回调)+ onEndReachedThreshold(触发阈值)实现 |
🖥️ 开发板触控灵敏度低,阈值建议设为0.2;真机/模拟器设为0.1 |
核心避坑点(新手高频错误)🚨
- 重复加载 :必须通过
isLoadingMore状态控制,加载中时禁止触发新请求; - 状态错乱:下拉刷新时需重置页码、错误状态、无更多数据状态;
- 阈值过大:默认阈值0.5易导致多次触发,建议0.1-0.2。
2.2 多场景加载提示的逻辑闭环
完整的列表交互需覆盖4类场景,实现状态无缝切换:
成功
失败
有更多数据
无更多数据
加载失败
初始加载
加载中🔄
请求结果
渲染列表📋
显示加载失败+重试按钮🔴
上拉加载
显示加载更多中...
显示已加载全部✅
显示加载失败+重试🔴
下拉刷新
重置列表+重新请求🔄
2.3 三方库适配要点(react-native-MJRefresh)📦
原生交互样式简单,实际开发中可选用适配鸿蒙的三方库优化体验:
- 版本选择 :必须用
3.0.0版本(亲测兼容RN 0.72.7 + 鸿蒙SDK 8.0)✅; - 集成逻辑:无需手动链接(RN 0.70+自动适配),直接包裹FlatList即可;
- 鸿蒙优化:降低开发板加载动画复杂度,避免卡顿🚧。
2.4 鸿蒙多终端适配差异🖥️
| 终端类型 | 适配调整 | 具体操作 |
|---|---|---|
| 模拟器 | 基础适配 | 阈值0.1,默认动画速度 |
| 鸿蒙真机 | 屏幕适配 | 通过Dimensions获取屏幕尺寸,动态设置列表高度 |
| DAYU200开发板 | 性能优化 | 简化列表项样式、关闭复杂动画、阈值设为0.2 |
💻 三、实操步骤(50分钟,重点环节)
本环节基于上一课时的用户列表,从状态管理→原生下拉刷新→上拉加载→三方库集成逐步推进,所有代码均可直接复制使用。
3.1 步骤1:分页接口适配+完善状态管理(5分钟)🔧
1.1 打开App.js,补充分页相关状态
javascript
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, FlatList, RefreshControl, TouchableOpacity, Dimensions } from 'react-native';
import service from './src/api/request';
const App = () => {
// 原有基础状态(上一课时)
const [userList, setUserList] = useState([]); // 📋 用户列表数据
const [loading, setLoading] = useState(true); // ⏳ 初始化加载状态
const [error, setError] = useState(''); // ❌ 错误信息
// 新增分页&交互状态(核心)
const [page, setPage] = useState(1); // 📄 当前页码
const [limit, setLimit] = useState(5); // 📊 每页条数(方便测试)
const [hasMore, setHasMore] = useState(true); // 📈 是否有更多数据
const [isRefreshing, setIsRefreshing] = useState(false); // 🔄 下拉刷新状态
const [isLoadingMore, setIsLoadingMore] = useState(false); // 📥 上拉加载状态
// 📏 屏幕尺寸(适配多终端)
const { height, width } = Dimensions.get('window');
return (
<View style={styles.container}>
{/* 后续补充列表渲染 */}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingHorizontal: 15
}
});
export default App;
1.2 封装分页请求方法(复用Axios)
在App.js中添加请求方法(核心逻辑):
javascript
// 📡 封装分页请求方法
const fetchUserList = async (currentPage = 1, isRefresh = false) => {
try {
// 状态重置:下拉刷新时清空旧数据、重置状态
if (isRefresh) {
setIsRefreshing(true);
setPage(1);
setError(''); // 🧹 清空错误状态
setHasMore(true); // 📈 重置无更多数据状态
} else {
// 上拉加载时仅标记加载状态
setIsLoadingMore(true);
}
// 🚀 发起分页请求
const res = await service.get(`/users?_page=${currentPage}&_limit=${limit}`);
// 📊 分页逻辑:返回数据少于每页条数 → 无更多数据
if (res.length < limit) {
setHasMore(false);
} else {
setHasMore(true);
}
// 📝 数据更新:下拉刷新重置列表,上拉加载追加列表
setUserList(prev => isRefresh ? res : [...prev, ...res]);
setLoading(false);
} catch (err) {
// ❌ 错误处理:区分下拉/上拉的错误提示
setError(err.message);
setLoading(false);
} finally {
// 🛑 结束加载状态(无论成功/失败)
setIsRefreshing(false);
setIsLoadingMore(false);
}
};
// 🔄 初始化请求(组件挂载时执行)
useEffect(() => {
fetchUserList(1);
}, []);
3.2 步骤2:原生FlatList实现下拉刷新(10分钟)🔄
2.1 完整列表渲染+下拉刷新配置
替换App.js中的返回部分,添加FlatList和RefreshControl:
javascript
// 🎨 列表项渲染函数(抽离成独立函数,优化性能)
const renderUserItem = ({ item }) => (
<TouchableOpacity style={styles.itemCard} activeOpacity={0.9}>
<Text style={styles.itemName}>{item.name}</Text>
<Text style={styles.itemEmail}>📧 {item.email}</Text>
<Text style={styles.itemPhone}>📱 {item.phone}</Text>
</TouchableOpacity>
);
// 🖥️ 页面渲染
return (
<View style={styles.container}>
{loading ? (
<Text style={styles.loadingText}>初始化加载中...</Text>
) : error ? (
<TouchableOpacity
style={styles.errorContainer}
onPress={() => fetchUserList(1)}
>
<Text style={styles.errorText}>{error}</Text>
<Text style={styles.retryText}>点击重试🔄</Text>
</TouchableOpacity>
) : userList.length === 0 ? (
<Text style={styles.emptyText}>📭 暂无用户数据</Text>
) : (
<FlatList
data={userList}
keyExtractor={(item) => item.id.toString()}
renderItem={renderUserItem}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.listContent}
// 🔄 下拉刷新核心配置(适配鸿蒙)
refreshControl={
<RefreshControl
refreshing={isRefreshing} // 📍 刷新状态绑定
onRefresh={() => fetchUserList(1, true)} // 🔄 下拉触发刷新
tintColor="#007AFF" // 🎨 鸿蒙主题色
title="下拉刷新中..."
titleColor="#666"
colors={['#007AFF']} // 🎨 安卓/鸿蒙加载动画颜色
/>
}
/>
)}
</View>
);
2.2 补充样式配置
javascript
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
paddingHorizontal: 15
},
loadingText: {
flex: 1,
textAlign: 'center',
textAlignVertical: 'center',
fontSize: 16,
color: '#666'
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
errorText: {
fontSize: 16,
color: '#ff4444',
marginBottom: 10
},
retryText: {
fontSize: 14,
color: '#007AFF',
textDecorationLine: 'underline'
},
emptyText: {
flex: 1,
textAlign: 'center',
textAlignVertical: 'center',
fontSize: 16,
color: '#999'
},
listContent: {
paddingVertical: 10
},
itemCard: {
padding: 15,
borderBottomWidth: 1,
borderBottomColor: '#f0f0f0',
marginBottom: 10,
borderRadius: 8,
backgroundColor: '#fafafa'
},
itemName: {
fontSize: 17,
fontWeight: '600',
color: '#222'
},
itemEmail: {
fontSize: 14,
color: '#666',
marginTop: 5
},
itemPhone: {
fontSize: 14,
color: '#888',
marginTop: 3
}
});
2.3 验证下拉刷新
bash
# 🔄 重启工程
react-native run-ohos --emulator
✅ 验证标准:
- 下拉列表触发刷新,显示"下拉刷新中...";
- 刷新完成后列表重置为第一页数据;
- 断网测试:下拉刷新显示错误提示,点击重试可重新请求。
2.4 Git提交📜
bash
git add .
git commit -m "feat: 实现原生下拉刷新功能,适配鸿蒙终端"
3.3 步骤3:原生FlatList实现上拉加载+多场景提示(15分钟)📥
3.1 添加上拉加载配置
修改FlatList组件,添加onEndReached、onEndReachedThreshold和ListFooterComponent:
javascript
<FlatList
// 原有属性不变
// 📥 上拉加载核心配置
onEndReached={() => {
// 🚫 防重复加载:无更多/加载中/下拉刷新时,禁止触发
if (!hasMore || isLoadingMore || isRefreshing) return;
fetchUserList(page + 1); // 📄 加载下一页
setPage(prev => prev + 1); // 📈 更新页码
}}
onEndReachedThreshold={0.1} // 📏 模拟器/真机阈值(开发板需改为0.2)
// 🎨 列表底部加载提示(多场景)
ListFooterComponent={() => {
// 📋 优先级:加载中 → 加载失败 → 无更多数据
if (isLoadingMore) {
return <Text style={styles.footerText}>加载更多中...</Text>;
} else if (!hasMore && userList.length > 0) {
return <Text style={styles.footerText}>已加载全部数据✅</Text>;
} else if (error && userList.length > 0) {
return (
<TouchableOpacity
style={styles.footerError}
onPress={() => fetchUserList(page)}
>
<Text style={styles.footerErrorText}>加载失败,点击重试🔄</Text>
</TouchableOpacity>
);
}
return <View style={styles.footerEmpty} />; // 🧱 空占位,避免列表跳动
}}
/>
3.2 补充底部提示样式
javascript
// 🎨 在styles中添加
footerText: {
padding: 15,
textAlign: 'center',
fontSize: 14,
color: '#666'
},
footerError: {
padding: 15,
textAlign: 'center'
},
footerErrorText: {
fontSize: 14,
color: '#ff4444'
},
footerEmpty: {
height: 10 // 🧱 空占位高度,避免列表触底跳动
}
3.3 多终端适配(开发板专属)🖥️
若测试开发板,需调整阈值并优化性能:
javascript
// 📏 动态设置阈值(根据终端类型)
const threshold = Platform.OS === 'ohos' && isBoard ? 0.2 : 0.1;
// FlatList中替换阈值
onEndReachedThreshold={threshold}
// 🚀 优化列表项渲染(开发板)
const renderUserItem = React.memo(({ item }) => (
// 原有列表项代码,减少嵌套和复杂样式
));
3.4 验证上拉加载
✅ 验证标准:
- 模拟器/真机:上拉列表到底部,显示"加载更多中...",加载完成后追加数据;
- 加载到最后一页(第2页,共2页),显示"已加载全部数据✅";
- 断网测试:上拉加载显示"加载失败,点击重试🔄",点击后重新请求;
- 开发板:调整阈值为0.2,确保上拉能稳定触发,无卡顿。
3.4 步骤4:集成react-native-MJRefresh三方库(15分钟,可选)📦
原生样式简单,集成三方库优化交互体验,适配鸿蒙多终端。
4.1 安装适配版本
bash
# 📂 进入工程目录
cd rnHarmonyDemo
# 📦 安装指定版本(兼容RN 0.72.7 + 鸿蒙)
npm install react-native-mjrefresh@3.0.0 --save
4.2 验证安装
- 查看
package.json,确认dependencies包含:"react-native-mjrefresh": "^3.0.0"✅; - 无需手动链接(RN 0.70+自动适配)🔗。
4.3 替换原生交互为三方库
javascript
// 📦 导入三方库
import MJRefresh from 'react-native-mjrefresh';
// 🎨 替换FlatList为MJRefresh包裹的列表
return (
<View style={styles.container}>
{loading ? (
<Text style={styles.loadingText}>初始化加载中...</Text>
) : error ? (
<TouchableOpacity
style={styles.errorContainer}
onPress={() => fetchUserList(1)}
>
<Text style={styles.errorText}>{error}</Text>
<Text style={styles.retryText}>点击重试🔄</Text>
</TouchableOpacity>
) : userList.length === 0 ? (
<Text style={styles.emptyText}>📭 暂无用户数据</Text>
) : (
<MJRefresh
// 🔄 下拉刷新配置
onRefresh={() => fetchUserList(1, true)}
isRefreshing={isRefreshing}
// 📥 上拉加载配置
onLoadMore={() => {
if (!hasMore || isLoadingMore || isRefreshing) return;
fetchUserList(page + 1);
setPage(prev => prev + 1);
}}
isLoadingMore={isLoadingMore}
// 🎨 自定义提示文本(适配鸿蒙)
loadMoreText="加载更多中..."
noMoreDataText="已加载全部数据✅"
failureText="加载失败,点击重试🔄"
onLoadMoreFailureRetry={() => fetchUserList(page)}
// 🚀 开发板性能优化
refreshSpeed="normal"
enableLoadMore={hasMore}
>
<FlatList
data={userList}
keyExtractor={(item) => item.id.toString()}
renderItem={renderUserItem}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.listContent}
ListFooterComponent={null} // 🧹 取消原生底部提示
/>
</MJRefresh>
)}
</View>
);
4.4 鸿蒙终端优化🖥️
javascript
// 📏 适配开发板屏幕尺寸
const MJRefreshWrapper = () => {
const { height } = Dimensions.get('window');
return (
<MJRefresh
style={{ height: height - 20 }} // 📏 动态设置高度
// 其他配置不变
>
{/* FlatList */}
</MJRefresh>
);
};
4.5 验证三方库
✅ 验证标准:
- 下拉刷新:显示自定义动画,触发后重置列表;
- 上拉加载:底部提示样式优化,加载失败时点击重试可重新请求;
- 开发板:动画流畅无卡顿,触控响应正常🚀。
3.5 步骤5:Git规范提交(5分钟)📜
bash
# 📂 添加所有修改
git add .
# 📜 规范提交
git commit -m "feat: 实现列表上拉加载+下拉刷新,添加多场景提示,集成MJRefresh"
# 🚀 推送到远程分支
git push origin feature-list-interaction
✅ 验证:打开代码仓库,确认feature-list-interaction分支提交记录正常,代码可拉取运行。
❌ 四、常见问题与解决方案(10分钟,新手必看)
🚫 问题1:下拉刷新不触发,控制台无异常
✅ 解决方案:
- 确认FlatList的父容器有
flex:1样式(避免列表被压缩); - 检查
refreshing状态是否与isRefreshing正确绑定; - 鸿蒙真机需开启应用"悬浮窗"权限(设置→应用→权限管理→悬浮窗)🪟;
- 测试代码:在
onRefresh中添加console.log('触发下拉刷新'),确认是否执行。
🚫 问题2:上拉加载多次触发,出现重复数据
✅ 解决方案(核心:防抖+状态控制):
javascript
// ⏳ 1. 添加防抖函数
const debounce = (func, delay) => {
let timer = null;
return (...args) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => func(...args), delay);
};
};
// 📥 2. 包装上拉加载回调
const handleLoadMore = debounce(() => {
if (!hasMore || isLoadingMore || isRefreshing) return;
fetchUserList(page + 1);
setPage(prev => prev + 1);
}, 500); // 500ms防抖
// 📋 3. FlatList中使用防抖后的回调
onEndReached={handleLoadMore}
🚫 问题3:加载提示切换错乱(如下拉时显示上拉提示)
✅ 解决方案:
-
明确状态优先级:
isRefreshing为true时,隐藏所有上拉提示; -
下拉刷新时重置所有状态:
javascriptif (isRefresh) { setIsRefreshing(true); setPage(1); setError(''); setHasMore(true); setIsLoadingMore(false); // 🔑 关键:重置上拉加载状态 } -
ListFooterComponent按优先级判断:
javascriptif (isRefreshing) return null; // 🔄 下拉刷新时隐藏底部提示 if (isLoadingMore) return <Text>加载更多中...</Text>; // 其他判断...
🚫 问题4:集成MJRefresh后报错「Cannot read property 'MJRefresh' of undefined」
✅ 解决方案:
-
确认版本为3.0.0,删除
node_modules和package-lock.json重新安装:bashrm -rf node_modules package-lock.json npm cache clean --force npm install -
检查导入路径:
import MJRefresh from 'react-native-mjrefresh'(无拼写错误)🔤; -
重启Metro服务:
npx react-native start --reset-cache🔄。
🚫 问题5:开发板列表交互卡顿,上拉触发延迟
✅ 解决方案:
-
优化列表渲染性能:
javascript// 🚀 使用memo缓存列表项 const renderUserItem = React.memo(({ item }) => ( // 简化列表项样式,减少嵌套 <View style={styles.simpleItem}> <Text>{item.name}</Text> </View> )); -
调整开发板专属配置:
javascript<FlatList initialNumToRender={3} // 📋 初始只渲染3项 maxToRenderPerBatch={5} // 📊 每次批量渲染5项 onEndReachedThreshold={0.2} // 📏 提高阈值 /> -
关闭三方库复杂动画:
javascript<MJRefresh refreshType="default" // 🎨 使用简单动画 loadMoreType="default" />
📝 五、课堂小结(5分钟)
本课时核心完成了列表交互的进阶开发,重点掌握4个核心要点:
- 原生FlatList实现上拉/下拉的关键是状态控制+阈值适配 ,通过
isLoadingMore/isRefreshing避免重复请求; - 多场景加载提示需实现逻辑闭环,覆盖加载中、失败、无更多数据等场景,提升用户体验🎨;
- 三方库集成的核心是版本适配+鸿蒙优化,优先选择兼容RN 0.72.7的版本,开发板需简化动画;
- 鸿蒙多终端适配需差异化调整,针对模拟器/真机/开发板的触控和性能特点优化配置🖥️。
列表交互是RN鸿蒙应用的核心能力,本节课实现的功能可满足大部分开发场景,下一节课我们将学习底部选项卡开发,完善应用页面结构🧱。
✅ 六、课后任务(必做)
任务1:复盘核心功能📝
独立完成「原生下拉刷新+上拉加载+多场景提示」全流程,重点掌握:
- 状态控制逻辑(避免重复加载);
- 多终端阈值适配(模拟器0.1/开发板0.2)。
任务2:优化交互体验🎨
- 动态设置阈值:通过
Dimensions获取屏幕高度,根据屏幕尺寸自动调整onEndReachedThreshold; - 列表项点击事件:点击列表项弹出用户ID和名称(使用
Alert组件)📢; - 加载动画优化:修改
RefreshControl的颜色为鸿蒙官方主题色(#007DFF)。
任务3:三方库深度集成📦
- 完成
react-native-MJRefresh集成,对比原生实现与三方库的差异; - 自定义三方库的加载动画样式,贴合鸿蒙设计规范🎨。
任务4:Git规范提交📜
bash
git add .
git commit -m "opt: 优化列表交互体验,适配鸿蒙多终端屏幕"
git push origin feature-list-interaction
任务5:预习📚
预习RN底部选项卡开发:
react-native-tab-navigator(基础);@react-navigation/bottom-tabs(进阶)。
🎯 核心要点总结
- 状态控制 :下拉刷新重置所有状态,上拉加载通过
isLoadingMore防重复请求🔄; - 阈值适配:模拟器/真机设0.1,开发板设0.2,适配不同触控灵敏度📏;
- 多场景提示:按"加载中→失败→无更多"优先级显示,实现逻辑闭环🎨;
- 三方库适配 :使用
react-native-mjrefresh@3.0.0,开发板简化动画避免卡顿🚀。
若你在实操中遇到列表交互、三方库集成、开发板适配问题,欢迎在评论区留言💬!下一节课我们一起解锁底部选项卡开发,实现多页面平滑切换~🚀
关注我,后续课时持续更新,从0到1掌握RN兼容鸿蒙跨平台开发!
欢迎加入开源鸿蒙跨平台社区,https://openharmonycrossplatform.csdn.net