Expo-Camera的onCameraReady触发异常:仅关闭相机或加console.log时执行
Expo-Camera onCameraReady 触发异常问题解决方法
核心解决思路
别依赖console.log这种临时hack,本质是React状态更新和相机硬件初始化的时序冲突问题。换个思路:用useEffect配合相机激活状态来处理比例检测,避开onCameraReady里的同步逻辑阻塞。
完整修复代码示例
import React, { useState, useRef, useEffect } from 'react'; import { View, Text, Dimensions } from 'react-native'; import { Camera } from 'expo-camera'; export default function QRCodeScanner() { const [hasPermission, setHasPermission] = useState(null); const [cameraRatio, setCameraRatio] = useState('4:3'); const cameraRef = useRef(null); const [cameraIsReady, setCameraIsReady] = useState(false); const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); const screenRatio = screenHeight / screenWidth; // 请求相机权限 useEffect(() => { (async () => { const { status } = await Camera.requestCameraPermissionsAsync(); setHasPermission(status === 'granted'); })(); }, []); // 相机就绪后再检测最优比例 useEffect(() => { if (cameraIsReady && cameraRef.current) { const fetchOptimalRatio = async () => { const supportedRatios = await cameraRef.current.getSupportedRatiosAsync(); // 筛选和屏幕比例最接近的相机比例 const bestRatio = supportedRatios.reduce((best, curr) => { const [w, h] = curr.split(':').map(Number); const ratio = h / w; return Math.abs(ratio - screenRatio) < Math.abs(best.ratio - screenRatio) ? { ratio, value: curr } : best; }, { ratio: Infinity, value: supportedRatios[0] }); setCameraRatio(bestRatio.value); }; fetchOptimalRatio(); } }, [cameraIsReady, screenRatio]); const handleCameraReady = () => { // 只做状态标记,不处理复杂逻辑 setCameraIsReady(true); }; if (hasPermission === null) return <View />; if (hasPermission === false) return <Text>没有相机权限,请在设置中开启</Text>; return ( <Camera ref={cameraRef} style={{ flex: 1 }} ratio={cameraRatio} onCameraReady={handleCameraReady} type={Camera.Constants.Type.back} /> ); }
问题根源解释
- Expo-Camera的
onCameraReady在相机硬件刚初始化完成时触发,但如果回调里直接做同步状态更新或异步请求,可能会因为React的批量更新机制,导致后续逻辑被吞掉。 - 加
console.log能临时解决,是因为它触发了JS引擎的微任务调度,把后续逻辑推到事件循环的下一轮,此时相机状态已经完全稳定,所以能正常执行。但这只是巧合,不是正经解决方案。
额外适配技巧
- 不要硬编码比例,根据当前设备的屏幕宽高比去匹配相机支持的比例,这样能保证预览画面完全铺满屏幕,不会变形。
- 如果还是遇到时序问题,可以在
useEffect里加个100ms左右的微小延迟(极端情况可用,不推荐作为常规方案):
useEffect(() => { if (cameraIsReady && cameraRef.current) { const timer = setTimeout(async () => { // 比例检测逻辑 }, 100); return () => clearTimeout(timer); } }, [cameraIsReady]);
内容的提问来源于stack exchange,提问作者somfink
相关产品推荐
相关产品推荐

