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

Expo-camera onBarCodeScanned仅触发一次问题求助

问题根因

Expo Camera 原生层的条码扫描监听器,在onBarcodeScanned属性从函数切换为undefined、再切回函数的场景下,存在监听器未被正确重新绑定的残留bug,即便在标注已修复的Expo 45.0.6版本中,部分安卓、iOS机型上依然会复现首次扫码后监听失效的问题,本质是靠动态切换回调属性启停扫码的实现逻辑本身不稳定。

推荐解决方案

不要通过给onBarcodeScanned传undefined的方式解绑监听,改用JS层节流锁控制扫码结果的处理逻辑,全程给Camera传入稳定的回调引用,从根源上避免原生监听器的绑定/解绑操作。

  • 用useRef存储扫码锁定状态,修改ref值不会触发组件重渲染,也不会导致Camera组件的props发生变化
  • 用useCallback包裹扫码回调,保证回调引用全程稳定,原生层扫码监听器仅在组件挂载时初始化一次,不会出现重绑定失败的问题
  • 扫码成功后先激活锁,拦截后续扫码结果,3秒冷却时间结束后自动解锁,恢复结果处理

对应实现代码:

import { useState, useRef, useCallback } from 'react';
import { View, Text } from 'react-native';
import { Camera } from 'expo-camera';

const QRScanner = () => {
    const [result, setResult] = useState('Nothing scanned yet');
    // 扫码锁ref,初始为未锁定状态
    const scanLockedRef = useRef(false);

    const scanHandler = useCallback(({ type, data }) => {
        // 锁激活时直接跳过结果处理
        if (scanLockedRef.current) return;

        // 首次拿到结果后立刻上锁,避免重复触发
        scanLockedRef.current = true;
        setResult(data);
        
        setTimeout(() => {
            // 3秒冷却结束后解锁,恢复扫码能力
            scanLockedRef.current = false;
        }, 3000);
    }, []);

    return (
        <View style={{flex: 1}}>
            <Camera
                style={{flex: 1}}
                // 始终传入稳定的回调引用,禁止传undefined触发原生监听解绑
                onBarcodeScanned={scanHandler}
            >
                <Text>{result}</Text>
            </Camera>
        </View>
    );
}
备选方案(特殊场景使用)

如果业务场景需要在冷却期完全停止摄像头的扫码检测(不只是拦截JS层结果),可以通过给Camera组件加key属性的方式,在恢复扫码时强制组件重新挂载,彻底重置原生层状态:

import { useState } from 'react';
import { View, Text } from 'react-native';
import { Camera } from 'expo-camera';

const QRScanner = () => {
    const [scanned, setScanned] = useState(false);
    const [result, setResult] = useState('Nothing scanned yet');
    const [cameraKey, setCameraKey] = useState(0);

    const scanHandler = ({type, data}) => {
        setScanned(true);
        setResult(data);
        
        setTimeout(() => {
            setScanned(false);
            // 递增key强制Camera重挂载,重置原生所有监听状态
            setCameraKey(prev => prev + 1);
        }, 3000);
    }

    return (
        <View style={{flex: 1}}>
            <Camera
                key={cameraKey}
                style={{flex: 1}}
                onBarcodeScanned={scanned ? undefined : scanHandler}
            >
                <Text>{result}</Text>
            </Camera>
        </View>
    )
}

注意:该方案会触发相机重新初始化,可能出现短暂黑屏,体验弱于第一种锁方案,非必要不使用。

内容的提问来源于stack exchange,提问作者mvioole

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 11:27:22