React Native中如何用Async Storage构建可新增的Workout数组?
解决React Native Async Storage Workout数组问题
核心问题分析
你当前的代码存在三个关键问题:
- 把训练的每个字段(名称、组数等)单独存在AsyncStorage中,每次提交会直接覆盖旧值,无法保存多条训练记录
- AsyncStorage的
getItem是异步操作,你没有加await,导致拿到的是Promise对象而非实际数据 - 状态管理错误,应该维护一个训练数组而非单个字段的状态
解决方案代码
以下是修改后的完整代码,实现类似TodoList的新增训练、实时刷新功能:
1. WorkoutTile 页面(训练提交页)
import { View, TextInput, Button, Alert } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { useState } from 'react'; const WorkoutTile = ({ navigation }) => { const [name, setName] = useState(''); const [sets, setSets] = useState(''); const [reps, setReps] = useState(''); const [weight, setWeight] = useState(''); const onSubmit = async () => { try { // 验证必填字段 if (!name || !sets || !reps || !weight) { Alert.alert('提示', '请填写所有训练信息'); return; } // 读取已存储的训练数组 const storedWorkouts = await AsyncStorage.getItem('workouts'); const workouts = storedWorkouts ? JSON.parse(storedWorkouts) : []; // 组装新训练项(添加唯一ID方便后续操作) const newWorkout = { id: Date.now().toString(), name, sets: parseInt(sets), reps: parseInt(reps), weight: parseFloat(weight), }; // 更新数组并存回AsyncStorage workouts.push(newWorkout); await AsyncStorage.setItem('workouts', JSON.stringify(workouts)); // 返回主页面 navigation.navigate('Body'); } catch (error) { Alert.alert('错误', error.message); } }; return ( <View style={{ padding: 20, marginTop: 20 }}> <TextInput placeholder="训练名称" value={name} onChangeText={setName} style={{ borderBottomWidth: 1, marginBottom: 12, paddingVertical: 8 }} /> <TextInput placeholder="组数" value={sets} onChangeText={setSets} keyboardType="numeric" style={{ borderBottomWidth: 1, marginBottom: 12, paddingVertical: 8 }} /> <TextInput placeholder="次数" value={reps} onChangeText={setReps} keyboardType="numeric" style={{ borderBottomWidth: 1, marginBottom: 12, paddingVertical: 8 }} /> <TextInput placeholder="重量" value={weight} onChangeText={setWeight} keyboardType="numeric" style={{ borderBottomWidth: 1, marginBottom: 20, paddingVertical: 8 }} /> <Button title="提交训练" onPress={onSubmit} /> </View> ); }; export default WorkoutTile;
2. Body 页面(主展示页)
import { View, Text, FlatList, StyleSheet, Button, Alert } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { useState, useEffect } from 'react'; const Body = ({ navigation }) => { const [workouts, setWorkouts] = useState([]); // 组件加载和页面聚焦时刷新训练数据 useEffect(() => { loadWorkouts(); const unsubscribe = navigation.addListener('focus', loadWorkouts); return unsubscribe; }, [navigation]); const loadWorkouts = async () => { try { const storedWorkouts = await AsyncStorage.getItem('workouts'); if (storedWorkouts) { setWorkouts(JSON.parse(storedWorkouts)); } } catch (error) { Alert.alert('错误', error.message); } }; // 渲染单个训练卡片 const renderWorkoutItem = ({ item }) => ( <View style={styles.tile}> <Text style={styles.tileTitle}>{item.name}</Text> <Text>组数: {item.sets}</Text> <Text>次数: {item.reps}</Text> <Text>重量: {item.weight}kg</Text> </View> ); return ( <View style={styles.container}> <Button title="添加新训练" onPress={() => navigation.navigate('WorkoutTile')} /> <FlatList data={workouts} keyExtractor={(item) => item.id} renderItem={renderWorkoutItem} style={{ marginTop: 20 }} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, padding: 20, marginTop: 20, }, tile: { backgroundColor: '#f5f5f5', padding: 16, borderRadius: 8, marginBottom: 10, }, tileTitle: { fontSize: 18, fontWeight: '600', marginBottom: 6, }, }); export default Body;
关键改进点说明
- 统一存储数组:用
workouts作为唯一key存储整个训练数组,避免单字段覆盖问题 - 异步操作规范:所有AsyncStorage操作都加
await,确保拿到实际数据而非Promise - 实时刷新:监听页面焦点事件,从提交页返回时自动加载最新数据,无需重启App
- 数据类型处理:将组数、次数转为整数,重量转为浮点数,避免字符串类型导致的渲染错误
- 唯一ID:给每个训练项添加基于时间戳的ID,保证FlatList渲染的唯一性
内容的提问来源于stack exchange,提问作者Kyle
相关产品推荐
相关产品推荐

