【问题标题】:React useState hook issue on updating real time data inside MQTT communication在 MQTT 通信中更新实时数据时反应 useState 挂钩问题
【发布时间】:2021-07-26 03:56:13
【问题描述】:

故事

我正在使用 React Js 和 MQTT 通信在地图中显示大量实时位置数据。你可以把它想象成一个仪表板,显示所有 Uber 司机的实时位置。

我有 7 台设备将其数据广播到 Rabbit MQ 服务器。在 Rabbit MQ 中,我安装了 MQTT 插件,以便我可以使用 React 中的 MQTT 从 Rabbit MQ 服务器检索数据。

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

假设这是从一台设备广播一次的示例数据:

{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 的array
  • 如果项目具有在array 中找不到的设备ID,则将项目添加到array
  • 如果项目具有在array 中找到的设备 ID,则该项目将替换数组中的旧项目(具有相同的设备 ID)。
  1. 使用useState 挂钩将data(状态变量)更新为array

代码

代码如下:

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/+'中的+是所有主题的通配符。

依赖:

"mqtt": "^4.2.8",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",

环境:

OS: Windows 10 Home
NPM: v6.14.8
Node: v12.9.0

问题:

data 变量只会像这个日志一样永远更新两次。

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。如果我打开datadata 的长度大于1,如下所示:

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. 如何解决或达到目标?

注意

如果这个案例不是很清楚,请在评论区告诉我。我会尽快更新问题。

【问题讨论】:

    标签: javascript reactjs react-hooks mqtt


    【解决方案1】:

    我认为您在关闭和变异状态方面遇到问题。

    由于您只在挂载时注册事件处理程序,您的message 事件处理程序将始终具有与array 相同的引用。

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

    由于引用没有改变,React 会假设数组没有更新。

    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>
        )
    }
    

    【讨论】:

    • 你说得对,我忘记了关闭和变异状态。我尝试使用您的代码,但发现代码存在另一个问题。 dataarray 变量从未更新。它只包含从 MQTT 服务器读取的第一个项目/消息。
    • 我已将[setData]useEffect 挂钩的参数)更改为[data][](空参数),但还是一样。
    • 我找到了实现目标的解决方案。让我更新您的答案,以便为我们特别是我做笔记。谢谢。
    猜你喜欢
    • 2021-05-13
    • 2019-07-31
    • 2021-10-02
    • 2021-03-30
    • 2021-04-13
    • 2020-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多