【问题标题】:useState hook's set State function not working properly with socket.iouseState 钩子的 set State 函数无法在 socket.io 中正常工作
【发布时间】:2020-06-23 18:03:38
【问题描述】:

我有一个使用 react hooks 和 socketio 的组件:

import React, { useState, useEffect } from "react";
import io from "socket.io-client";

const Live = () => {
  const [messages, setMessages] = useState([]);
  const ENDPOINT = "localhost:5000";

  useEffect(() => {
    socket = io(ENDPOINT);
    socket.on("message", (message) => {
      setMessages((messages) => [message, ...messages]);
      //setMessages([message, ...messages]); //It doesn't work
    });
  }, [ENDPOINT]);

  // rest of the code

};

如果我使用setMessages([message, ...messages]),它不起作用,并且在收到来自套接字的新消息后,messages 数组中的所有先前消息都消失了。这种行为背后的原因是什么?

【问题讨论】:

  • messages 在注释掉的更新中变得陈旧。使用 updater 函数可确保您拥有最新的状态副本。
  • Socket.io 是异步的,因此如果您不使用函数形式,消息将是函数首次调用时消息的闭包。
  • 那么,特别是在什么情况下我应该使用它来增加额外的功能?
  • 您将一个函数传递给 socket.on 并且在该函数中 messages 可用作闭包。但是当组件重新渲染时,效果不会重新运行,因此消息是stale closure。当您将函数传递给状态设置器时,React 会使用当前状态值调用该函数,这就是 setMessages((messages) => [message, ...messages]); 起作用的原因。
  • 你的效果应该返回一个函数,当组件被卸载或效果重新运行时断开连接。

标签: javascript reactjs socket.io frontend react-hooks


【解决方案1】:

我不推荐 useState 在这里做你正在做的事情。相反,请考虑useReducer

const Live = () => {
  const ENDPOINT = "localhost:5000";

  const [{ messages }, dispatch] = useReducer((state, action) => {
    if (action.type === 'NEW_MESSAGE') {
      state.messages.push(action.message)
      return state
    }
    return state
  }, { messages: [] })

  useEffect(() => {
    socket = io(ENDPOINT);
    socket.on("message", (message) => {
      dispatch({ type: 'NEW_MESSAGE', message })
    });
  }, [ENDPOINT])

  // rest of the code
};

来自useReducer 上的 React 文档

当您有涉及多个子值的复杂状态逻辑或下一个状态取决于前一个状态时,useReducer 通常比 useState 更可取。

在您的情况下,下一个 messages 状态取决于前一个 messages 状态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-16
    • 2012-12-03
    • 1970-01-01
    • 2021-02-21
    • 2021-10-20
    • 1970-01-01
    • 2021-06-20
    • 2022-12-01
    相关资源
    最近更新 更多