Expo BarCodeScanner导航返回后相机持续开启未关闭问题
问题根因
相机资源无法释放的核心原因有三点:
- react-navigation Stack 导航默认缓存所有访问过的页面实例,页面返回上一级时不会触发组件卸载,你之前用
children传入render prop的写法不会改变默认缓存逻辑,相机组件实例会一直留在内存中。 - 仅靠
useIsFocused做条件渲染无法彻底回收Expo BarCodeScanner的原生相机资源:该组件原生层的相机占用不会仅因为JS层移除节点就释放,必须配合组件卸载逻辑才能完全断开占用。 - 原有组件存在逻辑bug:
hasPermission初始值设为undefined,但加载态判断写的是hasPermission === null,该判断永远不会命中,会导致权限未授权完成时就提前初始化相机。
修复方案
1. 修改导航配置,开启页面失焦自动卸载
给QR扫描页的Stack.Screen添加unmountOnBlur: true配置,让页面离开导航栈视图时自动销毁组件实例,替换原有children传参写法,直接用component属性绑定组件即可:
<NavigationContainer fallback={<Text>Loading...</Text>}> <Stack.Navigator screenOptions={({ route, navigation }) => ({ headerShadowVisible: false, headerTitle: () => ( <Text style={{ fontSize: 30, fontFamily: Font["900"], }} > Test </Text> ), headerRight: () => route.name !== "QR" ? ( <Pressable onPress={() => navigation.navigate("QR")}> <QrcodeIcon size={26} color="black" /> </Pressable> ) : null, })} > <Stack.Screen name="Home" component={Home} /> <Stack.Screen name="QR" component={QRCode} options={{ unmountOnBlur: true }} /> </Stack.Navigator> </NavigationContainer>
注意:
unmountOnBlur是@react-navigation/native-stack的支持属性,如果你用的是旧版JS实现的@react-navigation/stack,建议替换为native-stack,原生栈对资源释放的处理更稳定,性能也更好。
2. 修复QRCode组件逻辑,补全状态判断和资源回收
修复权限判断的bug,保留聚焦状态判断作为双重保险,监听页面失焦事件重置扫描状态,确保相机实例可以正常销毁:
import { useIsFocused, useNavigation } from '@react-navigation/native'; const QRCode = () => { const [hasPermission, setHasPermission] = useState<boolean>(null); const [scanned, setScanned] = useState<boolean>(false); const isFocused = useIsFocused(); const linkTo = useLinkTo(); const navigation = useNavigation(); useEffect(() => { (async () => { const { status } = await BarCodeScanner.requestPermissionsAsync(); setHasPermission(status === "granted"); })(); // 组件卸载时重置扫描状态 return () => { setScanned(false); } }, []); // 页面失焦时主动重置扫描状态,避免下次进入无法扫描 useEffect(() => { const unsubscribe = navigation.addListener('blur', () => { setScanned(false); }); return unsubscribe; }, [navigation]); const handleBarCodeScanned = ({ type, data }: BarCodeEvent) => { setScanned(true); linkTo(data); }; if (hasPermission === null) { return <Text>Requesting for camera permission</Text>; } if (hasPermission === false) { return <Text>No access to camera</Text>; } return ( <View style={styles.container}> {/* 双重校验:仅页面聚焦时渲染扫描器 */} {isFocused ? ( <BarCodeScanner onBarCodeScanned={scanned ? undefined : handleBarCodeScanned} style={StyleSheet.absoluteFill} /> ) : null} </View> ); };
验证结果
修改完成后重新加载应用,从扫描页返回首页后1-2秒,系统状态栏的“Expo Go is using your camera”提示就会自动消失,相机资源被正常回收。
内容的提问来源于stack exchange,提问作者Sudhanshu Bhagwat
相关产品推荐
相关产品推荐

