Expo SecureStore存储异常求助(React Native + TypeScript)
Expo扫码应用商品数量重置问题修复
应用概况与问题
- 正在开发集成Stripe和Expo条形码扫描的移动应用,核心逻辑:
- 用户授权相机后,扫描含商品ID的条形码;商品存在则显示数量增减按钮,数量变更后存入Expo SecureStore(以商品ID为键)
- 遇到的两个问题:
- 用React Navigation切换页面返回,重扫同一商品时数量直接重置为0
- 未授予相机权限时,操作商品列表的+/-按钮也会出现数量重置
相关代码文件
ItemListComponent.tsx
import { Button, FlatList, View, Text } from 'react-native'; import * as SecureStore from 'expo-secure-store'; import { useState } from 'react'; export const ItemComponent = (props: any) => { const [amount, setAmount] = useState<number>(0); const getAmount = async () => { const amount = await SecureStore.getItemAsync(props.item.id.toString()); if (amount) { setAmount(parseInt(amount)); } getAmount(); } const save = async () => { await SecureStore.setItemAsync(props.item.id.toString(), amount.toString()); } return ( <View> <Text>{props.item.name}</Text> <Button onPress={() => { setAmount(amount + 1); save(); }} title='+' /> {amount > 0 && <Button onPress={() => { setAmount(amount - 1); save(); }} title='-' /> } </View> ); }; export const ItemListComponent = (props: any) => { return ( <FlatList data={props.items} renderItem={({ item }) => <ItemComponent key={item.id} item={item} /> } /> ); };
BarCodeScannerComponent.tsx
import { BarCodeScanner } from 'expo-barcode-scanner'; import { useState } from 'react'; import { StyleSheet } from 'react-native'; import { ItemComponent } from './ItemListComponent'; import Items from '../models/ItemsModel'; export const BarCodeScannerComponent = () => { const [item, setItem] = useState<Items>(); const getItem = async ({ data }: any) => { const response = await fetch(`http://192.168.1.81:8000/items/${data}`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); if (response.ok) { const json = await response.json(); setItem(json); } } return ( <View style={styles.container}> <BarCodeScanner onBarCodeScanned={getItem} style={StyleSheet.absoluteFillObject} /> {(item !== null && item !== undefined) && <ItemComponent key={item.id} item={item} />} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'column', justifyContent: 'center', }, });
问题修复步骤
1. 解决getAmount无限递归与初始值读取失效
原ItemComponent里的getAmount函数在内部递归调用自己,会导致无限循环,而且没有在组件挂载时自动读取存储的数量。改用useEffect在组件挂载时读取一次:
修改后的ItemComponent代码:
import { Button, View, Text } from 'react-native'; import * as SecureStore from 'expo-secure-store'; import { useState, useEffect } from 'react'; export const ItemComponent = (props: any) => { const [amount, setAmount] = useState<number>(0); // 组件挂载/商品ID变化时,读取存储的数量 useEffect(() => { const fetchStoredAmount = async () => { const storedValue = await SecureStore.getItemAsync(props.item.id.toString()); if (storedValue) { setAmount(parseInt(storedValue)); } }; fetchStoredAmount(); }, [props.item.id]); // 接收最新数量参数,避免闭包捕获旧值 const saveAmount = async (newAmount: number) => { await SecureStore.setItemAsync(props.item.id.toString(), newAmount.toString()); } return ( <View> <Text>{props.item.name}</Text> <Button onPress={() => { const newVal = amount + 1; setAmount(newVal); saveAmount(newVal); }} title='+' /> {amount > 0 && <Button onPress={() => { const newVal = amount - 1; setAmount(newVal); saveAmount(newVal); }} title='-' /> } </View> ); }; // ItemListComponent部分保留原代码即可
2. 修复状态更新的闭包问题
原代码中save函数直接使用amount状态,因为闭包特性可能会捕获到旧的状态值,导致存储的数据不正确。改成传递最新计算的数量给saveAmount函数,确保存储的是当前最新值。
3. 优化扫码组件的重复扫描问题
原扫码组件会持续识别条形码,导致多次触发getItem,频繁重置item状态,进而让ItemComponent反复挂载。添加扫描状态控制,成功扫描后暂时停止识别:
修改后的BarCodeScannerComponent:
import { BarCodeScanner } from 'expo-barcode-scanner'; import { useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { ItemComponent } from './ItemListComponent'; import Items from '../models/ItemsModel'; export const BarCodeScannerComponent = () => { const [item, setItem] = useState<Items>(); const [isScanned, setIsScanned] = useState(false); const getItem = async ({ data }: any) => { if (isScanned) return; // 已扫描过就不再处理 setIsScanned(true); const response = await fetch(`http://192.168.1.81:8000/items/${data}`, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); if (response.ok) { const json = await response.json(); setItem(json); } // 2秒后恢复扫描,可根据需求调整时间 setTimeout(() => setIsScanned(false), 2000); } return ( <View style={styles.container}> <BarCodeScanner onBarCodeScanned={isScanned ? undefined : getItem} style={StyleSheet.absoluteFillObject} /> {(item !== null && item !== undefined) && <ItemComponent key={item.id} item={item} />} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'column', justifyContent: 'center', }, });
4. 解决未授权相机时的列表数量重置
商品列表中的ItemComponent已经通过useEffect在挂载时读取存储值,切换页面返回后组件会重新挂载,自动读取最新的数量,自然解决了重置问题。
内容的提问来源于stack exchange,提问作者Zorp
相关产品推荐
相关产品推荐

