React Native iOS端useState配合回调函数失效问题求助
React Native iOS端useState更新后BLE扫描回调失效问题解决
问题场景
在iOS 17、Xcode 15、React 18.2.0、React Native 0.72.7环境下,使用react-native-ble-plx的startDeviceScan(或自定义searchDevices函数)时,首次调用setState更新设备列表后,扫描回调不再触发,Android端无此异常。
核心原因
iOS端BLE扫描回调受组件渲染周期影响:当useState更新触发组件重渲染后,原回调因闭包捕获旧变量引用,导致扫描逻辑被意外终止;或react-native-ble-plx在iOS端的扫描实例未在重渲染后正确保留引用。
解决方案
方案1:用useRef保存稳定的回调与管理器引用
通过useRef存储回调和BleManager实例,避免组件重渲染时引用丢失:
import { useState, useEffect, useRef } from 'react'; import { Button } from 'react-native'; import { BleManager } from 'react-native-ble-plx'; const BleScanner = () => { const [devices, setDevices] = useState([]); // 用useRef保存BleManager实例,避免重复创建 const managerRef = useRef(new BleManager()); // 存储稳定的扫描回调引用 const scanCallbackRef = useRef((device) => { console.info('Triggered'); // 函数式更新,避免闭包捕获旧状态 setDevices(prev => { if (!prev.some(d => d.id === device.id)) { return [...prev, device]; } return prev; }); }); useEffect(() => { console.log('devices', devices); }, [devices]); const searchDevicesPress = () => { // 先停止已有扫描 managerRef.current.stopDeviceScan(); // 启动扫描并使用稳定的回调 managerRef.current.startDeviceScan(null, null, (error, device) => { if (error) { console.error(error); return; } if (device) scanCallbackRef.current(device); }); }; return <Button onPress={searchDevicesPress} title="Search" />; }; export default BleScanner;
方案2:用useCallback控制扫描函数引用+生命周期清理
通过useCallback保证扫描函数引用稳定,同时在组件卸载或重新扫描时清理旧任务:
import { useState, useEffect, useCallback } from 'react'; import { Button } from 'react-native'; import { BleManager } from 'react-native-ble-plx'; const BleScanner = () => { const [devices, setDevices] = useState([]); const manager = new BleManager(); // 组件卸载时停止扫描 useEffect(() => { return () => manager.stopDeviceScan(); }, [manager]); useEffect(() => { console.log('devices', devices); }, [devices]); // 用useCallback保证函数引用稳定 const searchDevicesPress = useCallback(() => { manager.stopDeviceScan(); manager.startDeviceScan(null, null, (error, device) => { if (error) { console.error(error); return; } if (device) { console.info('Triggered'); setDevices(prev => { if (!prev.some(d => d.id === device.id)) { return [...prev, device]; } return prev; }); } }); }, [manager]); return <Button onPress={searchDevicesPress} title="Search" />; }; export default BleScanner;
额外排查项
- 确认iOS权限配置:
Info.plist中需添加NSBluetoothAlwaysUsageDescription和NSBluetoothPeripheralUsageDescription权限描述 - 更新依赖:将
react-native-ble-plx升级到支持iOS 17的最新版本
内容的提问来源于stack exchange,提问作者Jogy Felix
相关产品推荐
相关产品推荐

