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

不使用useEffect解决useState异步导致的蓝牙扫描列表重复问题

问题根源

你遇到的问题本质是两个原因叠加:

  1. React的状态更新是异步批量处理的,直接调用setDeviceList([])后状态不会立刻生效
  2. 若存在上一轮未结束的扫描,旧的扫描回调会持续触发,闭包捕获的旧状态会污染新的列表

以下两种方案都不需要使用useEffect,从根源解决问题:

方案1:扫描批次标记法(无需额外ref,不侵入原有逻辑)

通过给状态增加扫描批次ID,让回调仅处理当前批次的更新,完全依赖React状态的函数式更新特性实现:

  1. 调整state结构,增加扫描批次标识:
const [deviceState, setDeviceState] = useState({ scanId: 0, list: [] });
// 原有获取设备列表的逻辑直接取 deviceState.list 即可,无需修改其他业务代码
  1. 改造扫描逻辑:
const scanForDevices = () =>  {
    // 先停止上一轮未结束的扫描,避免无效回调占用资源
    manager.stopDeviceScan();
    // 生成当前扫描的唯一ID,会被本次扫描的所有回调闭包捕获
    const currentScanId = Date.now();
    // 重置状态为当前批次+空列表
    setDeviceState({ scanId: currentScanId, list: [] });

    manager.startDeviceScan(null, null, (error, device) => {
        if (error) return;
        setDeviceState(old => {
            // 非当前批次的更新直接丢弃,彻底避免旧扫描回调污染新列表
            if (old.scanId !== currentScanId) return old;
            return {
                ...old,
                list: [...old.list, device]
            };
        });
    });
}

方案2:同步镜像ref法(无需修改原有state结构)

通过useRef维护和state完全同步的本地列表镜像,ref的更新是同步的,不受React状态异步更新的影响:

const [deviceList, setDeviceList] = useState([]);
// 维护设备列表的同步镜像,更新实时生效
const deviceListRef = useRef(deviceList);

const scanForDevices = () =>  {
    manager.stopDeviceScan();
    // 同步清空镜像,无需等待React state更新完成
    deviceListRef.current = [];
    setDeviceList([]);

    manager.startDeviceScan(null, null, (error, device) => {
        if (error) return;
        // 所有更新先操作同步镜像,保证值的准确性
        const newList = [...deviceListRef.current, device];
        deviceListRef.current = newList;
        // 再同步到state触发视图渲染
        setDeviceList(newList);
    });
}

两种方案都彻底避免了异步更新导致的旧值问题,不需要额外做列表去重处理。

内容的提问来源于stack exchange,提问作者T3db0t

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:54:04