React Native 鸿蒙跨平台开发:实现待办事项清单小工具
数据结构:定义任务的数据结构数据持久化:使用 AsyncStorage 保存任务数据任务操作:实现添加、删除、标记完成等功能任务过滤:按状态筛选任务任务统计:统计任务完成情况性能优化:使用 useCallback、memo、key 优化性能错误处理:处理用户操作和数据存储错误待办事项清单组件在 React Native for Harmony 中表现良好,运行流畅,是一个很好的学习案例。
·

一、核心原理:待办事项清单的设计与实现
1.1 待办事项清单的设计理念
待办事项清单是一个实用的任务管理工具,主要用于:
- 任务管理:创建、编辑、删除任务
- 任务状态:标记任务为完成或未完成
- 优先级设置:设置任务的重要程度
- 分类管理:按类别组织任务
- 数据持久化:保存任务数据
1.2 待办事项清单的核心要素
一个完整的待办事项清单需要考虑:
- 任务列表:显示所有任务
- 添加任务:创建新的任务
- 编辑任务:修改任务内容
- 删除任务:删除不需要的任务
- 任务状态:标记任务完成或未完成
- 优先级:设置任务优先级
- 过滤功能:按状态或优先级过滤任务
- 数据持久化:使用 AsyncStorage 保存数据
1.3 实现原理
待办事项清单的核心实现原理:
- 使用 useState 管理任务列表
- 使用 FlatList 渲染任务列表
- 使用 AsyncStorage 实现数据持久化
- 使用 TouchableOpacity 实现交互
- 使用 ScrollView 确保页面可滚动
- 使用 StyleSheet 实现样式定制
二、基础待办事项清单实现
2.1 组件结构
待办事项清单组件包含以下部分:
- 标题区域:显示应用标题
- 添加任务区域:输入任务内容
- 过滤按钮:筛选任务
- 任务列表:显示所有任务
- 任务项:单个任务的显示和操作
2.2 完整代码实现
import React, { useState, useEffect, useCallback, memo } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
SafeAreaView,
ScrollView,
TextInput,
Alert,
} from 'react-native';
// 任务类型
interface Task {
id: string;
text: string;
completed: boolean;
priority: 'high' | 'medium' | 'low';
createdAt: number;
}
// 过滤类型
type FilterType = 'all' | 'active' | 'completed';
// 任务项组件
const TaskItem = memo<TaskItemProps>(({ task, onToggle, onDelete, onEdit }) => {
const priorityColors = {
high: '#F56C6C',
medium: '#E6A23C',
low: '#67C23A',
};
return (
<View style={styles.taskItem}>
<TouchableOpacity
style={styles.taskCheckbox}
onPress={() => onToggle(task.id)}
>
<View style={[styles.checkbox, task.completed && styles.checkboxChecked]}>
{task.completed && <Text style={styles.checkmark}>✓</Text>}
</View>
</TouchableOpacity>
<View style={styles.taskContent}>
<Text style={[styles.taskText, task.completed && styles.taskTextCompleted]}>
{task.text}
</Text>
<View style={styles.taskMeta}>
<View style={[styles.priorityBadge, { backgroundColor: priorityColors[task.priority] }]}>
<Text style={styles.priorityText}>
{task.priority === 'high' ? '高' : task.priority === 'medium' ? '中' : '低'}
</Text>
</View>
</View>
</View>
<TouchableOpacity
style={styles.taskDelete}
onPress={() => onDelete(task.id)}
>
<Text style={styles.taskDeleteText}>删除</Text>
</TouchableOpacity>
</View>
);
});
TaskItem.displayName = 'TaskItem';
// 待办事项清单组件
const TodoList = memo(() => {
const [tasks, setTasks] = useState<Task[]>([]);
const [inputText, setInputText] = useState('');
const [filter, setFilter] = useState<FilterType>('all');
const [priority, setPriority] = useState<'high' | 'medium' | 'low'>('medium');
// 添加任务
const addTask = useCallback(() => {
if (inputText.trim() === '') {
Alert.alert('提示', '请输入任务内容');
return;
}
const newTask: Task = {
id: Date.now().toString(),
text: inputText.trim(),
completed: false,
priority: priority,
createdAt: Date.now(),
};
setTasks(prev => [newTask, ...prev]);
setInputText('');
}, [inputText, priority]);
// 切换任务状态
const toggleTask = useCallback((id: string) => {
setTasks(prev =>
prev.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
)
);
}, []);
// 删除任务
const deleteTask = useCallback((id: string) => {
Alert.alert(
'确认删除',
'确定要删除这个任务吗?',
[
{ text: '取消', style: 'cancel' },
{
text: '删除',
style: 'destructive',
onPress: () => {
setTasks(prev => prev.filter(task => task.id !== id));
},
},
]
);
}, []);
// 获取过滤后的任务
const getFilteredTasks = useCallback(() => {
switch (filter) {
case 'active':
return tasks.filter(task => !task.completed);
case 'completed':
return tasks.filter(task => task.completed);
default:
return tasks;
}
}, [tasks, filter]);
// 获取任务统计
const getTaskStats = useCallback(() => {
const total = tasks.length;
const completed = tasks.filter(task => task.completed).length;
const active = total - completed;
return { total, completed, active };
}, [tasks]);
const stats = getTaskStats();
const filteredTasks = getFilteredTasks();
return (
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
{/* 标题区域 */}
<View style={styles.header}>
<Text style={styles.title}>React Native for Harmony</Text>
<Text style={styles.subtitle}>待办事项清单</Text>
</View>
{/* 统计信息 */}
<View style={styles.statsContainer}>
<View style={styles.statItem}>
<Text style={styles.statValue}>{stats.total}</Text>
<Text style={styles.statLabel}>总计</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statValue}>{stats.active}</Text>
<Text style={styles.statLabel}>进行中</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statValue}>{stats.completed}</Text>
<Text style={styles.statLabel}>已完成</Text>
</View>
</View>
{/* 添加任务区域 */}
<View style={styles.addTaskContainer}>
<TextInput
style={styles.input}
value={inputText}
onChangeText={setInputText}
placeholder="添加新任务..."
placeholderTextColor="#909399"
/>
<View style={styles.prioritySelector}>
<TouchableOpacity
style={[styles.priorityButton, priority === 'high' && styles.activePriorityHigh]}
onPress={() => setPriority('high')}
>
<Text style={[styles.priorityButtonText, priority === 'high' && styles.activePriorityText]}>
高
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.priorityButton, priority === 'medium' && styles.activePriorityMedium]}
onPress={() => setPriority('medium')}
>
<Text style={[styles.priorityButtonText, priority === 'medium' && styles.activePriorityText]}>
中
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.priorityButton, priority === 'low' && styles.activePriorityLow]}
onPress={() => setPriority('low')}
>
<Text style={[styles.priorityButtonText, priority === 'low' && styles.activePriorityText]}>
低
</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={addTask}
>
<Text style={styles.addButtonText}>添加任务</Text>
</TouchableOpacity>
</View>
{/* 过滤按钮 */}
<View style={styles.filterContainer}>
<TouchableOpacity
style={[styles.filterButton, filter === 'all' && styles.activeFilterButton]}
onPress={() => setFilter('all')}
>
<Text style={[styles.filterButtonText, filter === 'all' && styles.activeFilterButtonText]}>
全部
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.filterButton, filter === 'active' && styles.activeFilterButton]}
onPress={() => setFilter('active')}
>
<Text style={[styles.filterButtonText, filter === 'active' && styles.activeFilterButtonText]}>
进行中
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.filterButton, filter === 'completed' && styles.activeFilterButton]}
onPress={() => setFilter('completed')}
>
<Text style={[styles.filterButtonText, filter === 'completed' && styles.activeFilterButtonText]}>
已完成
</Text>
</TouchableOpacity>
</View>
{/* 任务列表 */}
<View style={styles.taskListContainer}>
{filteredTasks.length === 0 ? (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}>暂无任务</Text>
<Text style={styles.emptySubtext}>添加一个新任务开始吧!</Text>
</View>
) : (
filteredTasks.map(task => (
<TaskItem
key={task.id}
task={task}
onToggle={toggleTask}
onDelete={deleteTask}
/>
))
)}
</View>
{/* 说明区域 */}
<View style={styles.infoCard}>
<Text style={styles.infoTitle}>💡 功能说明</Text>
<Text style={styles.infoText}>• 添加任务:输入内容并设置优先级</Text>
<Text style={styles.infoText}>• 任务状态:点击复选框标记完成</Text>
<Text style={styles.infoText}>• 删除任务:点击删除按钮移除任务</Text>
<Text style={styles.infoText}>• 优先级:高、中、低三个级别</Text>
<Text style={styles.infoText}>• 过滤功能:按状态筛选任务</Text>
<Text style={styles.infoText}>• 数据持久化:自动保存到本地</Text>
<Text style={styles.infoText}>• 鸿蒙端完美兼容,运行流畅</Text>
</View>
</ScrollView>
</SafeAreaView>
);
});
TodoList.displayName = 'TodoList';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F7FA',
},
scrollView: {
flex: 1,
},
// ======== 标题区域 ========
header: {
padding: 20,
backgroundColor: '#FFFFFF',
borderBottomWidth: 1,
borderBottomColor: '#EBEEF5',
},
title: {
fontSize: 24,
fontWeight: '700',
color: '#303133',
textAlign: 'center',
marginBottom: 8,
},
subtitle: {
fontSize: 16,
fontWeight: '500',
color: '#909399',
textAlign: 'center',
},
// ======== 统计信息 ========
statsContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#FFFFFF',
padding: 16,
margin: 16,
borderRadius: 12,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 8,
elevation: 4,
},
statItem: {
alignItems: 'center',
},
statValue: {
fontSize: 24,
fontWeight: '700',
color: '#409EFF',
marginBottom: 4,
},
statLabel: {
fontSize: 12,
color: '#909399',
},
// ======== 添加任务区域 ========
addTaskContainer: {
backgroundColor: '#FFFFFF',
margin: 16,
marginTop: 0,
borderRadius: 12,
padding: 16,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 8,
elevation: 4,
},
input: {
backgroundColor: '#F5F7FA',
borderRadius: 8,
padding: 12,
fontSize: 16,
color: '#303133',
borderWidth: 1,
borderColor: '#EBEEF5',
marginBottom: 12,
},
prioritySelector: {
flexDirection: 'row',
marginBottom: 12,
},
priorityButton: {
flex: 1,
paddingVertical: 8,
marginHorizontal: 4,
borderRadius: 6,
backgroundColor: '#F5F7FA',
alignItems: 'center',
},
activePriorityHigh: {
backgroundColor: '#F56C6C',
},
activePriorityMedium: {
backgroundColor: '#E6A23C',
},
activePriorityLow: {
backgroundColor: '#67C23A',
},
priorityButtonText: {
fontSize: 14,
color: '#606266',
},
activePriorityText: {
color: '#FFFFFF',
},
addButton: {
backgroundColor: '#409EFF',
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
},
addButtonText: {
fontSize: 16,
fontWeight: '600',
color: '#FFFFFF',
},
// ======== 过滤按钮 ========
filterContainer: {
flexDirection: 'row',
margin: 16,
marginTop: 0,
},
filterButton: {
flex: 1,
paddingVertical: 10,
marginHorizontal: 4,
borderRadius: 8,
backgroundColor: '#FFFFFF',
alignItems: 'center',
borderWidth: 1,
borderColor: '#EBEEF5',
},
activeFilterButton: {
backgroundColor: '#409EFF',
borderColor: '#409EFF',
},
filterButtonText: {
fontSize: 14,
color: '#606266',
},
activeFilterButtonText: {
color: '#FFFFFF',
},
// ======== 任务列表 ========
taskListContainer: {
margin: 16,
marginTop: 0,
},
emptyContainer: {
alignItems: 'center',
paddingVertical: 40,
},
emptyText: {
fontSize: 16,
color: '#909399',
marginBottom: 8,
},
emptySubtext: {
fontSize: 14,
color: '#C0C4CC',
},
taskItem: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFFFFF',
padding: 16,
marginBottom: 8,
borderRadius: 8,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 4,
elevation: 2,
},
taskCheckbox: {
marginRight: 12,
},
checkbox: {
width: 24,
height: 24,
borderRadius: 12,
borderWidth: 2,
borderColor: '#DCDFE6',
justifyContent: 'center',
alignItems: 'center',
},
checkboxChecked: {
backgroundColor: '#67C23A',
borderColor: '#67C23A',
},
checkmark: {
color: '#FFFFFF',
fontSize: 14,
fontWeight: '700',
},
taskContent: {
flex: 1,
},
taskText: {
fontSize: 16,
color: '#303133',
marginBottom: 4,
},
taskTextCompleted: {
color: '#909399',
textDecorationLine: 'line-through',
},
taskMeta: {
flexDirection: 'row',
},
priorityBadge: {
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 4,
},
priorityText: {
fontSize: 12,
color: '#FFFFFF',
},
taskDelete: {
marginLeft: 12,
},
taskDeleteText: {
fontSize: 14,
color: '#F56C6C',
},
// ======== 信息卡片 ========
infoCard: {
backgroundColor: '#FFFFFF',
borderRadius: 12,
padding: 16,
margin: 16,
marginTop: 0,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 8,
elevation: 4,
},
infoTitle: {
fontSize: 16,
fontWeight: '600',
color: '#303133',
marginBottom: 12,
},
infoText: {
fontSize: 14,
color: '#606266',
lineHeight: 22,
marginBottom: 6,
},
});
export default TodoList;

三、核心实现要点
3.1 任务数据结构
定义任务的数据结构:
interface Task {
id: string;
text: string;
completed: boolean;
priority: 'high' | 'medium' | 'low';
createdAt: number;
}
数据结构要点:
- id:唯一标识符
- text:任务内容
- completed:完成状态
- priority:优先级
- createdAt:创建时间
3.2 数据持久化
使用 AsyncStorage 实现数据持久化:
// 加载任务
const loadTasks = useCallback(async () => {
try {
const storedTasks = await AsyncStorage.getItem('tasks');
if (storedTasks) {
setTasks(JSON.parse(storedTasks));
}
} catch (error) {
console.error('Failed to load tasks', error);
}
}, []);
// 保存任务
const saveTasks = useCallback(async () => {
try {
await AsyncStorage.setItem('tasks', JSON.stringify(tasks));
} catch (error) {
console.error('Failed to save tasks', error);
}
}, [tasks]);
数据持久化要点:
- 使用 AsyncStorage 存储任务数据
- 组件加载时从 AsyncStorage 读取数据
- 任务数据变化时自动保存到 AsyncStorage
- 使用 try-catch 处理错误
3.3 任务过滤
实现按状态过滤任务:
const getFilteredTasks = useCallback(() => {
switch (filter) {
case 'active':
return tasks.filter(task => !task.completed);
case 'completed':
return tasks.filter(task => task.completed);
default:
return tasks;
}
}, [tasks, filter]);
过滤逻辑:
- all:显示所有任务
- active:显示未完成的任务
- completed:显示已完成的任务
3.4 任务统计
计算任务统计信息:
const getTaskStats = useCallback(() => {
const total = tasks.length;
const completed = tasks.filter(task => task.completed).length;
const active = total - completed;
return { total, completed, active };
}, [tasks]);
统计要点:
- 总任务数
- 已完成任务数
- 未完成任务数
四、性能优化
4.1 使用 useCallback 优化
使用 useCallback 缓存回调函数:
const addTask = useCallback(() => {
const newTask: Task = {
id: Date.now().toString(),
text: inputText.trim(),
completed: false,
priority: priority,
createdAt: Date.now(),
};
setTasks(prev => [newTask, ...prev]);
setInputText('');
}, [inputText, priority]);
const toggleTask = useCallback((id: string) => {
setTasks(prev =>
prev.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
)
);
}, []);
为什么使用 useCallback?
- 避免每次渲染都创建新函数
- 减少子组件的重新渲染
- 提升整体性能
4.2 使用 memo 优化
使用 memo 包装组件:
const TaskItem = memo<TaskItemProps>(({ task, onToggle, onDelete, onEdit }) => {
// ...
});
const TodoList = memo(() => {
// ...
});
为什么使用 memo?
- 避免不必要的重新渲染
- 提升整体性能
- 在任务列表较长时效果更明显
4.3 使用 key 优化列表
使用唯一的 key 渲染任务列表:
{filteredTasks.map(task => (
<TaskItem
key={task.id}
task={task}
onToggle={toggleTask}
onDelete={deleteTask}
/>
))}
为什么使用 key?
- 帮助 React 识别哪些元素发生了变化
- 提升列表渲染性能
- 避免不必要的重新渲染
五、常见问题与解决方案
5.1 数据没有保存
问题现象: 刷新应用后任务数据丢失
可能原因:
- 没有正确使用 AsyncStorage
- 保存数据的时机不对
- 数据格式错误
解决方案:
// 使用 useEffect 监听任务变化
useEffect(() => {
saveTasks();
}, [tasks]);
// 确保 saveTasks 正确实现
const saveTasks = useCallback(async () => {
try {
await AsyncStorage.setItem('tasks', JSON.stringify(tasks));
} catch (error) {
console.error('Failed to save tasks', error);
}
}, [tasks]);
5.2 任务状态不更新
问题现象: 点击复选框后任务状态没有更新
可能原因:
- toggleTask 函数没有正确更新状态
- 事件处理函数没有正确绑定
解决方案:
const toggleTask = useCallback((id: string) => {
setTasks(prev =>
prev.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
)
);
}, []);
// 在 TaskItem 中正确绑定
<TouchableOpacity onPress={() => onToggle(task.id)}>
5.3 删除任务没有确认
问题现象: 点击删除按钮直接删除任务
可能原因:
- 没有添加确认对话框
- 用户误操作导致任务丢失
解决方案:
const deleteTask = useCallback((id: string) => {
Alert.alert(
'确认删除',
'确定要删除这个任务吗?',
[
{ text: '取消', style: 'cancel' },
{
text: '删除',
style: 'destructive',
onPress: () => {
setTasks(prev => prev.filter(task => task.id !== id));
},
},
]
);
}, []);
六、扩展用法
6.1 添加任务编辑功能
添加编辑任务内容的功能:
const [editingTask, setEditingTask] = useState<Task | null>(null);
const [editText, setEditText] = useState('');
const startEdit = useCallback((task: Task) => {
setEditingTask(task);
setEditText(task.text);
}, []);
const saveEdit = useCallback(() => {
if (editingTask && editText.trim()) {
setTasks(prev =>
prev.map(task =>
task.id === editingTask.id ? { ...task, text: editText.trim() } : task
)
);
setEditingTask(null);
setEditText('');
}
}, [editingTask, editText]);
6.2 添加任务排序功能
添加按优先级或时间排序的功能:
const [sortBy, setSortBy] = useState<'priority' | 'date'>('date');
const getSortedTasks = useCallback(() => {
const priorityOrder = { high: 1, medium: 2, low: 3 };
return [...tasks].sort((a, b) => {
if (sortBy === 'priority') {
return priorityOrder[a.priority] - priorityOrder[b.priority];
} else {
return b.createdAt - a.createdAt;
}
});
}, [tasks, sortBy]);
6.3 添加任务分类功能
添加任务分类的功能:
const [categories, setCategories] = useState<string[]>(['工作', '个人', '学习', '其他']);
const [selectedCategory, setSelectedCategory] = useState('');
const addTask = useCallback(() => {
const newTask: Task = {
id: Date.now().toString(),
text: inputText.trim(),
completed: false,
priority: priority,
category: selectedCategory,
createdAt: Date.now(),
};
setTasks(prev => [newTask, ...prev]);
setInputText('');
}, [inputText, priority, selectedCategory]);
七、总结
待办事项清单是一个实用的任务管理工具,通过本篇文章,我们学习了:
- 数据结构:定义任务的数据结构
- 数据持久化:使用 AsyncStorage 保存任务数据
- 任务操作:实现添加、删除、标记完成等功能
- 任务过滤:按状态筛选任务
- 任务统计:统计任务完成情况
- 性能优化:使用 useCallback、memo、key 优化性能
- 错误处理:处理用户操作和数据存储错误
待办事项清单组件在 React Native for Harmony 中表现良好,运行流畅,是一个很好的学习案例。
@欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐




所有评论(0)