使用来自 WEbsocket 的 onMessage 内的空状态来响应应用程序

发布于 2025-01-11 07:05:32 字数 3006 浏览 0 评论 0原文

我为我的公司创建了一个应用程序来管理不同的事情。 我首先开始使用类组件来实现它,现在切换到功能组件。

我面临的问题是,在 Websocket onMessage 内的一个函数中使用旧/空状态。

这就是当前应用程序的构建方式。

const DocumentControl = ({ createWebsocket, logOut }) => {
   const [elementList, setElementList] = useState([]);
   const webSocket = useRef(null);

   useEffect(() => {
      webSocket.current = createWebsocket();
      webSocket.current.onmessage = (messageEvent) => {
        console.log(elementList);
        if (devComm) {
          console.log(JSON.parse(messageEvent.data));
        }
        processMessage(JSON.parse(messageEvent.data));
      };
      window.addEventListener("beforeunload", () => {
        logOut(true);
      });
   }, []);

   useEffect(() => {
       loadElements();
   }, [menu]);

   const processMessage = (message) => {
      var newArr, index;
      switch (message.type) {
        case "document":
          var elementItem = message.data;
          newArr = [...elementList];   // here elementList is always []
          ...
          break;
        default:
          if (devComm) {
            console.log("---Unknown WSMessage!---");
          }
      }
  };
}

显然有更多的逻辑,但这可能足以描述我的问题。 基本上,如果菜单发生变化,我会从服务器加载我的 ElementList。 每当服务器端的元素更新时,我都会通过 Websockets 向所有客户端发送一条消息,因此所有客户端都是最新的。

---我的 问题---
从服务器加载元素并使用 setElementList 效果很好。但是每当我通过 websockets 收到消息时,状态 elementList 总是为空。

我制作了一个显示当前元素列表的按钮,并且列表是正确的。

例如:我的列表中有 4 个元素。由于 Websockets 中的消息,我想将新元素添加到当前列表中。但 elementList 是 [] 内部 processMessage,所以我将新元素添加到 [] 中,并且在 setElementList 之后所有其他元素都消失了(因为之前是空数组)

我的第一个想法是(因为我从父级获取了 Websocket 元素)也许它使用不同的 elementList 实例,该实例初始化为空 []。

但这没有意义,因为 setElementList 仍然影响“原始”elementList。

我还尝试将 elementList 与 useRef(elementList) 一起使用并使用 ref.current 访问它,但仍然没有改变结果。

以下是 createWebsocket 的实现方式:

const createWebsocket = () => {
    if (!webSocket.current) {
      var uri = baseURI.replace("http://", "ws://");

      console.log("Create new Websocket");
      console.log("Websocket: " + uri + "websocket/");
      
      let socket = new WebSocket(uri + "websocket/" + user.Token);
      socket.onopen = () => {};

      socket.onclose = (closeEvent) => {
        console.log("closed socket:");
        if (closeEvent.code !== 3001) {
          logOut(false);
          if (closeEvent !== null && closeEvent.reason.length > 0) {
            alert(closeEvent.reason);
          }
        }
      };

      socket.onerror = () => {
        logOut(true, false);
        alert("Something went wrong");
      };

      webSocket.current = socket;
    }
    return webSocket.current;
};

有什么想法为什么 elementList 在 ProcessMessage 中与组件的其他函数中不同?

--------- 更新 -------

我可以通过使用此解决方法临时修复它:

const elementsRef = useRef([]);

useEffect(() => {
    elementsRef.current = elementList;
  }, [elementList]);

然后访问 elementsRef.current 但必须有一个更优雅的解决方案

I've created a application for my company to manage diffrent things.
I first started realizing it with Class-Components, now switched to Functional components.

The issue that i'm facing is, that in one function inside Websocket onMessage uses old/empty state.

This is how the application currently is built.

const DocumentControl = ({ createWebsocket, logOut }) => {
   const [elementList, setElementList] = useState([]);
   const webSocket = useRef(null);

   useEffect(() => {
      webSocket.current = createWebsocket();
      webSocket.current.onmessage = (messageEvent) => {
        console.log(elementList);
        if (devComm) {
          console.log(JSON.parse(messageEvent.data));
        }
        processMessage(JSON.parse(messageEvent.data));
      };
      window.addEventListener("beforeunload", () => {
        logOut(true);
      });
   }, []);

   useEffect(() => {
       loadElements();
   }, [menu]);

   const processMessage = (message) => {
      var newArr, index;
      switch (message.type) {
        case "document":
          var elementItem = message.data;
          newArr = [...elementList];   // here elementList is always []
          ...
          break;
        default:
          if (devComm) {
            console.log("---Unknown WSMessage!---");
          }
      }
  };
}

There is obviously more logic but this may be sufficient to describe my problem.
Basically i'm loading from the Server my ElementList if the Menu changes.
Whenever an Element on the Server-Side is updated, I send a message through Websockets to all clients, so all are up-to-date.

---My Issue---
Loading the Elements from Server and using setElementList works fine. But whenever I receive a message through websockets, the state elementList is always empty.

I made a button which displays the current elementList and there the List is correctly.

For example: I have 4 Elements in my list. Because of Message in Websockets i want to add to my current list the new Element. But the elementList is [] inside processMessage, so I add the new element to an [] and after setElementList all other elements are gone (because of empty array previously)

My first thought was, that (because I get the Websocket element from parent) maybe it uses diffrent instance of elementList, which is initialized as empty [].

But it would not make sense, because setElementList still affects the "original" elementList.

I also tried using elementList with useRef(elementList) and accessing it with ref.current but still didn't change the outcome.

here is how the createWebsocket is implemented:

const createWebsocket = () => {
    if (!webSocket.current) {
      var uri = baseURI.replace("http://", "ws://");

      console.log("Create new Websocket");
      console.log("Websocket: " + uri + "websocket/");
      
      let socket = new WebSocket(uri + "websocket/" + user.Token);
      socket.onopen = () => {};

      socket.onclose = (closeEvent) => {
        console.log("closed socket:");
        if (closeEvent.code !== 3001) {
          logOut(false);
          if (closeEvent !== null && closeEvent.reason.length > 0) {
            alert(closeEvent.reason);
          }
        }
      };

      socket.onerror = () => {
        logOut(true, false);
        alert("Something went wrong");
      };

      webSocket.current = socket;
    }
    return webSocket.current;
};

Any Ideas why elementList is diffrent inside ProcessMessage then in other functions of the Component?

--------- UPDATE -------

I could temporary fix it, by using this workaround:

const elementsRef = useRef([]);

useEffect(() => {
    elementsRef.current = elementList;
  }, [elementList]);

and then accessing elementsRef.current
But there must be an more elegant soltuion to this

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文