React使用useState更新MQTT实时设备数据异常问题咨询
问题原因
- 闭包陷阱导致读取旧状态:useEffect依赖为空数组,仅在组件首次挂载时执行一次,内部绑定的MQTT消息回调始终引用首次渲染时的
data状态(初始空数组),后续状态更新后回调内无法拿到最新的data值,所有逻辑判断都基于旧值运行。 - 消息处理逻辑错误:
- 原有遍历逻辑为只要现有数组中存在任意一条数据ID和新消息ID不匹配就插入新消息,会导致同ID消息被重复插入
- 额外增加的
itemMessage['ble.beacons'] === null判断不符合给出的上报数据格式,会过滤掉大部分合法上报数据
- 数组引用异常:组件顶层声明的
array变量被首次渲染的闭包持续持有,所有新消息都往同一个数组对象中push。后续调用setData时传入的都是同一个数组引用,React判定状态没有变更就不会触发重渲染,因此data仅在前两次有引用变更时触发更新。控制台打印的是数组的引用,展开后显示的是实时的最新内容,和打印时的长度快照不符,就出现了你看到的日志异常。
修复方案
使用useState的函数式更新语法规避闭包问题,修改消息处理逻辑匹配业务需求,同时补充MQTT连接的卸载清理逻辑避免内存泄漏,修复后代码如下:
import React, { useEffect, useState } from 'react' import mqtt from 'mqtt' export default function RealtimeMarker() { const [data, setData] = useState([]) useEffect(() => { const url = 'ws://blahblahblah/ws' const client = mqtt.connect(url) const topicsToSubscribe = 'message/devices/+' client.on('connect', () => { console.log('connected') client.subscribe(topicsToSubscribe) }) client.on('message', (topic, message) => { const itemMessage = JSON.parse(message.toString()) console.log('itemMessage: ', itemMessage) // 函数式更新直接拿到最新的prev状态,规避闭包问题 setData(prev => { // 查找是否存在同ID设备 const existIndex = prev.findIndex(item => item['device.id'] === itemMessage['device.id']) if (existIndex > -1) { // 存在则替换对应位置的数据,返回新数组 const newData = [...prev] newData[existIndex] = itemMessage return newData } else { // 不存在则追加新数据,最多保留7条,可根据实际需求调整限制 return prev.length < 7 ? [...prev, itemMessage] : prev } }) }) // 组件卸载时清理MQTT连接,避免内存泄漏 return () => { client.unsubscribe(topicsToSubscribe) client.end() } }, []) console.log('data: ', data.length, data) return ( <div> </div> ) }
内容的提问来源于stack exchange,提问作者Jabal Logian
相关产品推荐
相关产品推荐

