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

Next.js中MQTT事件内useState的setData失效及WebSocket连接问题

Next.js中async-mqtt接收消息无法更新React状态问题

问题描述

在Next.js应用中使用async-mqtt库实现MQTT协议,能正常接收MQTT消息(日志显示在本地终端而非浏览器),但在MQTT的message事件中调用useState的setData方法时,UI无任何变化;手动点击测试按钮调用setData却能正常工作。尝试将data作为依赖项加入useEffect,也仅在手动设置data时触发。

编辑:按建议修改后,客户端出现新错误:WebSocket connection to 'ws://broker.hivemq.com/' failed

组件代码

"use client"
import React, { useEffect, useState } from 'react';

const MQTTSubscriber = () => {
const[data, setData] = useState()

    const MQTT = require("async-mqtt");
    const client = MQTT.connect("tcp://broker.hivemq.com");
    
    const doStuff = async () => {
      console.log("Starting");
      try {
        //await client.publish("my topic", "It works!");
        await client.subscribe("my topic")
        console.log("Hello world")
        // This line doesn't run until the server responds to the publish
        //await client.end();
        // This line doesn't run until the client has disconnected without error
        console.log("Done");
      } catch (e){
        // Do something about it!
        console.log(e.stack);
        process.exit();
    }

}

client.on("message", (topic, message) => {
 const payload = { topic, message: message.toString() };
 console.log(message.toString());
 setData(message.toString()) //Here the issue, nothing happens.
});

client.on("connect", doStuff);

return (
 <div>
   <button onClick={() => setData("Test")}> Set data manually test </button>
   <p>{"Data: " + data}</p>
 </div>
);
};

export default MQTTSubscriber;

package.json

{
  "name": "next-webgl-unity-mqtt",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "async-mqtt": "^2.6.3",
    "autoprefixer": "10.4.16",
    "bufferutil": "^4.0.7",
    "eslint": "8.49.0",
    "eslint-config-next": "13.5.2",
    "mqtt": "^5.0.5",
    "next": "13.5.2",
    "postcss": "8.4.30",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tailwindcss": "3.3.3",
    "utf-8-validate": "^5.0.10"
  }
}

原因与解决方法

核心问题分析

  1. MQTT客户端脱离React生命周期:组件函数内部直接创建客户端会导致每次重渲染都生成新实例,旧实例的事件监听捕获的是旧的setData函数,调用后无法触发组件重渲染;同时浏览器不支持tcp://协议,只能用WebSocket,所以日志只在终端输出。
  2. WebSocket连接参数错误:HiveMQ公共代理的WebSocket默认端口不是80,直接用ws://broker.hivemq.com会连接失败,需要指定正确端口。

修复后的代码

"use client"
import React, { useEffect, useState } from 'react';
import mqtt from 'async-mqtt';

const MQTTSubscriber = () => {
  const [data, setData] = useState('');

  useEffect(() => {
    // 组件挂载时创建MQTT客户端
    const client = mqtt.connect('ws://broker.hivemq.com:8000');

    // 消息处理函数
    const handleMessage = (topic, message) => {
      const payloadStr = message.toString();
      console.log(payloadStr);
      setData(payloadStr);
    };

    // 连接成功后的订阅逻辑
    const doStuff = async () => {
      try {
        await client.subscribe('my topic');
        console.log('已成功订阅主题');
      } catch (error) {
        console.error('订阅失败:', error);
      }
    };

    // 绑定事件监听
    client.on('connect', doStuff);
    client.on('message', handleMessage);

    // 组件卸载时清理资源
    return () => {
      client.off('message', handleMessage);
      client.end();
    };
  }, []); // 空依赖数组确保只执行一次

  return (
    <div>
      <button onClick={() => setData('Test')}>手动设置数据测试</button>
      <p>数据: {data}</p>
    </div>
  );
};

export default MQTTSubscriber;

关键修复点

  • 生命周期绑定:将客户端创建和事件监听放在useEffect内,确保组件挂载时仅初始化一次,避免重复实例和闭包问题。
  • WebSocket协议适配:使用ws://broker.hivemq.com:8000(明文)或wss://broker.hivemq.com:8883(加密)连接,适配浏览器环境。
  • 资源清理:在useEffect返回函数中移除事件监听并关闭客户端,防止内存泄漏。
  • 模块规范对齐:改用ES模块导入async-mqtt,符合React组件的模块使用规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 06:42:03