mqtt-react-hooks 订阅者在一条消息后停止工作

mqtt-react-hooks Subscriber stops working after one message

我有一个使用 mqtt-react-hooks 和 redux 的简单 React 应用程序。每次订阅者收到新消息时,我都想更新我的 redux 存储。

Subscriber.tsx

import React, { useEffect} from 'react';
import { useSubscription } from 'mqtt-react-hooks';
import { useAppDispatch } from '../features/item/hooks';
import { addItem } from '../features/item/item-slice';

const Subscriber = () => {
  const { message } = useSubscription('queue');
  const dispatch = useAppDispatch();
  
  useEffect(() => {
    if (message && message.message) {
      dispatch(addItem(JSON.parse(message.message)));
    } 
  }, [message]);

  
  return (
        <span>{message}</span>
  );
};

export default Subscriber

App.tsx

import React from 'react';
import './App.css';
import {useAppSelector} from './features/item/hooks'
import { Connector } from 'mqtt-react-hooks'
import Subscriber from './mqtt/Subscriber'

function App() {
  const items = useAppSelector((state) => state.item.items);

  return (
    <>
      <Connector brokerUrl="ws://localhost:9001"
      options={{keepalive: 10}}>
      <div className="item-holder">
        {Array.from(items, ([key, it]) => ({ key, it })).map( (kvp) => { return <div>{kvp.it} key={kvp.key}></div>})}
      </div>
      <Subscriber />
      </Connector>
    </>
  );
}

export default App;

如果我从订阅者中删除 useEffect,就会收到并更新消息。我可以发送任意数量的消息。但是,当我在 useEffect 中调用 dispatch(addItem(... 时,它会收到第一条消息,但会忽略所有以后的消息。我的 mosquitto 代理说客户端已关闭连接。它从不尝试重新连接。

我对反应很陌生。我有一种感觉,我根本没有做对。我真正想要的是一个 redux 存储,它根据来自 mqtt 主题的消息维护状态。该应用程序具有允许用户将消息发布回 mqtt 代理并更改 redux 状态的按钮。

编辑

根据要求,这是 addItem 代码。

import {createSlice, PayloadAction} from '@reduxjs/toolkit'

interface ItemState {
    items: Item[],
}

const initialState: ItemState = {
    items: []
}

const orderSlice = createSlice({
    name: 'items',
    initialState,
    reducers: {
        addItem(state, action: PayloadAction<Item>) {
          state.items.push(action.payload);
          
          return state;
        }
    }
});

export const { addItem } = itemSlice.actions;
export default itemSlice.reducer;

而 useAppDispatch 来自 ./features/item/hooks

import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { RootState, AppDispatch } from './item-store'

export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

但是,我要补充一点,我摆脱了这个并使用了通常的 useDispatch 和 useSelector 而不是“useApp____”版本并得到了相同的结果。

我认为问题出在 mqtt-react-hooks 钩子上,但我的反应能力还不够高,无法解决。

似乎发生了一系列事情导致 Connector 重新呈现并断开连接,这是我认为正在发生的事情:

  1. App 正在订阅存储状态,导致它在每次收到新消息时重新呈现。
  2. 每次 App 呈现时,您都在重新创建 mqtt 配置对象,因为您传递的是对象文字 {keepalive: 10}
  3. Connector.tsx第48行,mqttConnect回调依赖于mqtt选项对象。 React 进行引用相等性检查,发现选项已更改,并导致重新创建回调。
  4. Connector.tsx 第 59 行,这导致 useEffect 重新运行,因为回调发生了变化,它调用了它的 teardown 函数,从而结束了 mqtt 连接。

要修复它,您应该在 App 之外创建 MQTT 选项,这样它们就不会改变。