You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Expo SecureStore存储异常求助(React Native + TypeScript)

Expo扫码应用商品数量重置问题修复

应用概况与问题

  • 正在开发集成Stripe和Expo条形码扫描的移动应用,核心逻辑:
    1. 用户授权相机后,扫描含商品ID的条形码;商品存在则显示数量增减按钮,数量变更后存入Expo SecureStore(以商品ID为键)
    2. 遇到的两个问题:
      • 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 14:12:26