React Native中如何将AsyncStorage的JSON数据绑定到FlatList
问题修正点与修改方案
- 修正状态初始值:FlatList的
data属性仅接受数组类型,原代码初始值设为布尔值true会直接导致渲染异常,将状态初始值改为空数组,同时调整状态名提升可读性。 - 包裹异步请求逻辑:原代码直接在组件顶层调用
AsyncStorage.getItem,会在每次组件重渲染时重复触发请求,更新状态后又再次触发重渲染,陷入死循环。用useEffect加空依赖包裹该逻辑,保证仅在组件挂载时执行一次。 - 解析存储的字符串数据:AsyncStorage仅支持存储字符串类型,取出的JSON字符串需要通过
JSON.parse转换为JS对象才能正常使用。 - 取正确层级的列表数据:存储的对象结构中,
users字段才是FlatList需要的数组,不能直接将整个对象赋值给data属性。 - 补充
keyExtractor属性:FlatList要求每个列表项绑定唯一key,避免性能警告,直接使用每项的id作为唯一标识即可。
修改后完整代码
import React, { useState, useEffect } from 'react'; import { SafeAreaView, StyleSheet, View, Text, Image, Alert, TouchableOpacity } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import Icon from 'react-native-vector-icons/MaterialIcons'; import COLORS from '../../consts/colors'; import foods from '../../consts/foods'; import { PrimaryButton } from '../components/Button'; import AsyncStorage from '@react-native-async-storage/async-storage'; const CartScreen = ({ navigation }) => { // 调整状态初始值与命名 const [userList, setUserList] = useState([]); // 异步逻辑放入useEffect,仅挂载执行一次 useEffect(() => { const fetchStoredData = async () => { try { const value = await AsyncStorage.getItem('storeddata'); if (value) { // 解析字符串为对象,提取users数组 const parsedData = JSON.parse(value); setUserList(parsedData.users || []); console.log("saveddata", parsedData); } } catch (e) { console.log("读取存储数据失败", e); } }; fetchStoredData(); }, []); const CartCard = ({ item }) => { console.log("itemlist", item); return ( <View style={style.cartCard}> <Image source={{ uri: item.image }} style={{ height: 80, width: 80 }} /> <View style={{ height: 100, marginLeft: 10, paddingVertical: 20, flex: 1, }}> <Text style={{ fontWeight: 'bold', fontSize: 16 }}>{item.title}</Text> <Text style={{ fontSize: 13, color: COLORS.grey }}> {item.title} </Text> <Text style={{ fontSize: 17, fontWeight: 'bold' }}>${item.id}</Text> </View> <View style={{ marginRight: 20, alignItems: 'center' }}> <Text style={{ fontWeight: 'bold', fontSize: 18 }}>5</Text> <View style={style.actionBtn}> <Icon name="remove" size={25} color={COLORS.white} /> <Icon name="add" size={25} color={COLORS.white} /> </View> </View> </View> ); }; return ( <SafeAreaView style={{ backgroundColor: COLORS.white, flex: 1 }}> <View style={style.header}> <Icon name="arrow-back-ios" size={28} onPress={navigation.goBack} /> <Text style={{ fontSize: 20, fontWeight: 'bold' }}>Cart</Text> </View> <FlatList showsVerticalScrollIndicator={false} contentContainerStyle={{ paddingBottom: 80 }} data={userList} // 添加keyExtractor指定唯一key keyExtractor={item => item.id.toString()} renderItem={({ item }) => <CartCard item={item} />} ListFooterComponentStyle={{ paddingHorizontal: 20, marginTop: 20 }} ListFooterComponent={() => ( <View> <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginVertical: 15, }}> <Text style={{ fontSize: 18, fontWeight: 'bold' }}> Total Price </Text> <Text style={{ fontSize: 18, fontWeight: 'bold' }}>$50</Text> </View> <View style={{ marginHorizontal: 30 }}> <PrimaryButton title="CHECKOUT" onPress={() => navigation.navigate('Checkout')} /> </View> </View> )} /> </SafeAreaView> ); }; const style = StyleSheet.create({ header: { paddingVertical: 20, flexDirection: 'row', alignItems: 'center', marginHorizontal: 20, }, cartCard: { height: 100, elevation: 15, borderRadius: 10, backgroundColor: COLORS.white, marginVertical: 10, marginHorizontal: 20, paddingHorizontal: 10, flexDirection: 'row', alignItems: 'center', }, actionBtn: { width: 80, height: 30, backgroundColor: COLORS.primary, borderRadius: 30, paddingHorizontal: 5, flexDirection: 'row', justifyContent: 'center', alignContent: 'center', }, }); export default CartScreen;
内容的提问来源于stack exchange,提问作者kathir
相关产品推荐
相关产品推荐

