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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:45:37