React Native中通过回调更新useState数组的问题
解决React Native Expo蓝牙扫描重复设备及状态更新问题
问题根源分析
- 第一种写法中,蓝牙回调触发时,闭包捕获的
deviceList始终是组件初始渲染时的空数组(useState更新异步,回调不会自动获取最新state值),导致重复校验逻辑完全失效,会不断添加同一设备。 - 第二种写法里的普通数组
deviceList2会在组件每次重渲染时被重新初始化,且组件无法及时触发重渲染,导致后续扫描到的设备无法实时显示。
正确实现方案
使用useRef保存最新的设备ID集合(Set结构更适合快速查重),结合函数式setState更新UI状态,既解决闭包捕获旧值的问题,又保证状态实时更新。
import { useState, useRef } from 'react'; import { View, Text, TouchableOpacity } from 'react-native'; const DeviceListView = () => { const [deviceList, setDeviceList] = useState([]); // 用useRef存储已发现的设备ID,避免闭包捕获旧state的问题,Set结构查重效率更高 const discoveredDeviceIds = useRef(new Set()); const startScanning = () => { // 扫描前清空历史记录 discoveredDeviceIds.current.clear(); setDeviceList([]); manager.startDeviceScan(null, null, (error, device) => { if (error) { console.error('蓝牙扫描出错:', error); return; } // 校验设备是否已存在 if (!discoveredDeviceIds.current.has(device.id)) { console.log(`添加设备 ${device.id} 到列表`); const newDevice = { device: device, // 补充你的其他设备属性 }; // 更新已发现设备ID集合 discoveredDeviceIds.current.add(device.id); // 函数式更新state,确保基于最新状态修改 setDeviceList(prevList => [...prevList, newDevice]); } }); }; // 停止扫描,避免内存泄漏 const stopScanning = () => { manager.stopDeviceScan(); discoveredDeviceIds.current.clear(); }; const componentList = deviceList.map(item => ( <Text key={item.device.id} style={{ padding: 10 }}> 设备ID: {item.device.id} </Text> )); return ( <View style={{ flex: 1, paddingTop: 20 }}> <TouchableOpacity onPress={startScanning} style={{ padding: 10, backgroundColor: '#2196F3', marginBottom: 10 }} > <Text style={{ color: 'white', textAlign: 'center' }}>开始扫描</Text> </TouchableOpacity> <TouchableOpacity onPress={stopScanning} style={{ padding: 10, backgroundColor: '#F44336', marginBottom: 20 }} > <Text style={{ color: 'white', textAlign: 'center' }}>停止扫描</Text> </TouchableOpacity> {componentList} </View> ); }; export default DeviceListView;
核心要点说明
useRef的.current属性在组件生命周期内保持稳定,不会随重渲染重置,能实时获取最新的设备ID集合。- 使用
Set结构存储设备ID,查重操作的时间复杂度为O(1),比数组some方法更高效。 - 函数式
setDeviceList(prevList => [...prevList, newDevice])确保每次状态更新都基于最新的state值,避免异步更新导致的状态不一致。 - 必须添加停止扫描逻辑,在组件卸载或不需要扫描时调用,防止蓝牙扫描持续运行引发内存泄漏。
内容的提问来源于stack exchange,提问作者T3db0t
相关产品推荐
相关产品推荐

