首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在MQTT通信中实时更新数据时应对useState挂钩问题

在MQTT通信中实时更新数据时应对useState挂钩问题
EN

Stack Overflow用户
提问于 2021-07-26 03:56:13
回答 1查看 495关注 0票数 0

故事

我在地图上显示了大量实时位置数据,使用的是MQTT通信的React。你可以想象它就像一个仪表板,显示所有优步司机的实时位置。

我有7个设备将它们的数据广播到兔子MQ服务器。在兔子MQ中,我安装了MQTT插件,以便使用React中的MQTT从兔子MQ服务器检索数据。

来自每个设备的数据在一个随机时间被广播。所以一秒钟内不可能有数据,在一秒钟内会有很多来自某些设备的数据。

假设这个是从一个设备中播放的数据示例:

代码语言:javascript
复制
{ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}

or:

{ble.beacons: Array(0), channel.id: 10476, device.id: 3, device.name: "Device-3", device.type.id: 579, …}

目标

我想做的是:

  1. 过滤每个项目(从设备广播):假设我有一个array来存储每个设备。因为我有7个设备,所以我将有一个长度为7.
  • If的array。项目有一个在array中找不到的设备id,该项被添加到array.
  • If中,该项目有一个在array中找到的设备id,该项将使用useState钩子将array.
  1. Update array.
    1. Update data (状态变量)中的旧项(用相同的设备id)替换为array

代码

以下是代码:

代码语言:javascript
复制
import React, { useEffect, useState } from 'react'
import mqtt from 'mqtt'

export default function RealtimeMarker() {
    const [ data, setData ] = useState([])
    let array = []

    const usingMqtt = () => {
        const url = 'ws://blahblahblah/ws'
        const client = mqtt.connect(url)
        const topicsToSubscribe = 'message/devices/+' // '+' is a wildcard
        
        client.on('connect', () => {
            console.log('connected')
        client.subscribe(topicsToSubscribe)
        })

        client.on('message', (topic, message) => {
            const itemMessage = JSON.parse(message.toString())
            console.log('itemMessage: ', itemMessage)

            // ADD NEW ITEM TO EMPTY DATA
            if(data.length === 0) {
                array.push(itemMessage)
            }
            // ADD NEW ITEM TO NON-EMPTY DATA
            else {
                for(let i = 0; i < data.length; i++) {
                    if(data[i]['device.id'] !== itemMessage['device.id'] && itemMessage['ble.beacons'] === null) {
                        array.push(itemMessage)
                    }
                }
            }
            
            setData(array)
        })
    }

    console.log('data: ', data.length, data)

    useEffect(() => {
        usingMqtt()
    }, [])

    return (
        <div>
        </div>
    )
}

主题+中的'message/devices/+'是所有主题的通配符。

依赖关系:

代码语言:javascript
复制
"mqtt": "^4.2.8",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",

环境:

代码语言:javascript
复制
OS: Windows 10 Home
NPM: v6.14.8
Node: v12.9.0

问题:

data变量只更新两次,就像这个日志一样。

代码语言:javascript
复制
10:29:46.179 RealtimeMarker.js:65 data:  0 []
10:29:46.326 RealtimeMarker.js:34 connected
10:29:46.610 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:29:46.611 RealtimeMarker.js:65 data:  1 [{…}]
10:29:48.845 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.046, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:29:48.846 RealtimeMarker.js:65 data:  2 (2) [{…}, {…}]
10:29:49.021 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.043, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:29:49.022 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.041, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:29:49.023 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.048, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:29:49.024 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.033, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:29:51.946 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.071, …}
10:29:53.236 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.006, …}
10:29:53.410 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.007, …}
10:29:53.410 RealtimeMarker.js:42 itemMessage:  {ble.beacons: Array(0), channel.id: 10476, device.id: 3, device.name: "Device-3", device.type.id: 579, …}
10:29:53.410 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.6, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:29:53.410 RealtimeMarker.js:42 itemMessage:  {ble.beacons: Array(0), channel.id: 10476, device.id: 3, device.name: "Device-3", device.type.id: 579, …}
10:29:53.411 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.6, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:29:56.570 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:29:58.468 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.048, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:29:58.637 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.041, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:01.984 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.067, …}
10:30:03.177 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:03.353 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.008, …}
10:30:03.354 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.608, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:03.354 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.512, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:03.354 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.512, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:06.771 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:30:11.039 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.05, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:11.238 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.037, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:11.415 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.735, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:11.416 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.735, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:11.417 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.746, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:11.418 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.785, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:11.585 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.754, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:11.585 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:30:11.586 RealtimeMarker.js:42 itemMessage:  {ble.beacons: Array(0), channel.id: 10476, device.id: 5, device.name: "Device-5", device.type.id: 579, …}
10:30:12.462 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.008, …}
10:30:12.636 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:12.636 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.512, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:12.636 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.558, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:12.637 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.549, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:16.475 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:30:21.963 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.067, …}
10:30:22.577 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.006, …}
10:30:22.749 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:22.750 RealtimeMarker.js:42 itemMessage:  {ble.beacons: Array(0), channel.id: 10476, device.id: 3, device.name: "Device-3", device.type.id: 579, …}
10:30:22.751 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.595, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:22.752 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.562, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:26.452 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:30:31.486 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.067, …}
10:30:32.226 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:32.401 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.006, …}
10:30:32.401 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.571, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:32.402 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.567, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:32.402 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.578, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:34.401 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.039, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:34.568 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.039, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
10:30:36.795 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:30:40.088 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.854, battery.charging.status: false, battery.current: 0, battery.voltage: 4.011, …}
10:30:40.248 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.553, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:40.248 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.556, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:40.248 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.553, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:40.248 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.043, ain.2: 2.553, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
10:30:41.425 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:30:41.599 RealtimeMarker.js:42 itemMessage:  {ble.beacons: Array(0), channel.id: 10476, device.id: 5, device.name: "Device-5", device.type.id: 579, …}
10:30:44.862 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.735, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:45.023 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.735, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:45.024 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.746, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:45.024 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.785, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:45.024 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.173, ain.2: 2.754, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
10:30:46.328 RealtimeMarker.js:42 itemMessage:  {ain.1: 0.087, ain.2: 3.234, battery.charging.status: false, battery.current: 0, battery.voltage: 4.066, …}
10:30:47.899 RealtimeMarker.js:34 connected

正如您所看到的,data只更新了两次,尽管所有7个设备的数据都被传输到了React应用程序中。

还有一件事不清楚。您可以看到,在10:29:46.611中,数据的长度为1。如果打开data,则data的长度超过1,如下所示:

代码语言:javascript
复制
0: {ain.1: 0.173, ain.2: 2.974, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
1: {ain.1: 0.173, ain.2: 2.968, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
2: {ain.1: 0.173, ain.2: 2.972, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
3: {ain.1: 0.173, ain.2: 2.97, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
4: {ain.1: 0.173, ain.2: 2.955, battery.charging.status: false, battery.current: 0, battery.voltage: 4.004, …}
5: {ain.1: 0.087, ain.2: 3.033, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
6: {ain.1: 0.043, ain.2: 2.894, battery.charging.status: false, battery.current: 0, battery.voltage: 4.01, …}
7: {ain.1: 0.043, ain.2: 2.887, battery.charging.status: false, battery.current: 0, battery.voltage: 4.008, …}
8: {ain.1: 0.043, ain.2: 2.593, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}
9: {ain.1: 0.043, ain.2: 2.589, battery.charging.status: false, battery.current: 0, battery.voltage: 4.03, …}

问题

  1. 怎么会发生这种事?我的代码出了什么问题?
  2. 如何解决这个问题或实现目标?

备注

如果这个案子不太清楚,请在评论中告诉我。我很快就会更新这个问题。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-26 04:12:39

我认为你对封闭和变异状态有异议。

因为您只在挂载上注册事件处理程序,所以message事件的事件处理程序对array总是有相同的引用。

这意味着每次收到消息时,您都会将消息推送到相同的初始数组上,并将相同的引用存储到状态中。

由于引用没有改变,React将假定数组尚未更新。

代码语言:javascript
复制
import React, { useEffect, useState } from 'react'
import mqtt from 'mqtt'

export default function RealtimeMarker() {
    const [ data, setData ] = useState([])

    console.log('data: ', data.length, data)

    // Use useEffect to create client and event listener.
    // No need to recreate usingMqtt function on every render.
    useEffect(() => {
        const url = 'ws://blahblahblah/ws'
        const client = mqtt.connect(url)
        const topicsToSubscribe = 'message/devices/+' // '+' is a wildcard
        
        client.on('connect', () => {
            console.log('connected')
            client.subscribe(topicsToSubscribe);
        })

        client.on('message', (topic, message) => {
            const itemMessage = JSON.parse(message.toString())
            console.log('itemMessage: ', itemMessage)
            
            // make use of alternate syntax of setState to ensure you are always using
            // newest state values.
            setData(currentData => {
                // Create an array with a new reference.
                // Without a new reference react assumes there is no change to the array.
                let array = [...currentData]

                if(currentData.length === 0) {
                    array.push(itemMessage)
                }
                else {
                    let found = 0
                    for(let i = 0; i < currentData.length; i++) {
                        console.log(currentData[i]['device.id'], itemMessage['device.id'], itemMessage['ble.beacons'])
                        if(currentData[i]['device.id'] === itemMessage['device.id']) {
                            found++
                            currentData[i] = itemMessage
                        }
                    }

                    if(found === 0) {
                        array.push(itemMessage)
                    }
                }

                return array
            })
        })

        //Ensure client is closed on unmount of component.
        return () => {
            client.end();
        }
    }, []);

    return (
        <div>
        </div>
    )
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68524338

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档