reactjs - 为什么 useEffect() 似乎在从套接字接收数据后重置了我的本地状态?

标签 reactjs sockets socket.io react-hooks use-effect

我有一个简单的 react 组件,它通过 SocketIO 发送和接收消息。我的状态钩子(Hook)看起来像这样

 const [newMessage, createMessage] = useState('');
 const [messages, setMessage] = useState([]);
 const [isConnected, setConnection] = useState(false);

这个想法是用户可以通过套接字编写和发送消息,然后接收返回的消息。用户在文本区域中输入一些文本。消息的内容被保存到“newMessage”本地状态变量中 - 如果需要,实现细节如下 - 然后用户通过套接字发送 newMessage。

const sendMessage = () => {
    const currentMessage = {...newMessage}; //reduced for the sake of brevity

    socket.emit('message', currentMessage);
    createMessage('');
  };


const composeMessage = (
    <>
      <div>
        <textarea rows="2" cols="28" placeholder="Chat Message" onKeyDown={(e) => handleKeyDown(e)} onChange={(e) => createMessage(e.currentTarget.value)} value={newMessage} />
      </div>
      <div>
        <button onClick={sendMessage}><i className="ra ra-horn-call" /></button>
      </div>
    </>
  );

服务器端,消息被接收并立即传输回房间。我将监听消息响应的函数放入了useEffect钩子(Hook)中。这个想法是使用钩子(Hook)代替 componentDidMount,所以我最初通过传递一个空数组作为 useEffect 的第二个参数来实现它。当我这样做时,每当收到消息时,它都会清除我的“消息”状态,然后用新消息替换内容。从浏览器看来,一次只能保存一条消息 - 前一条消息不断被替换。我厌倦了在第二个参数数组中传递“消息”状态,现在消息似乎被附加到状态中,并且组件在收到时重新渲染 - 酷吧,这就是我想要的?

我注意到我在每条后续消息上都遇到性能问题,并最终发现每次渲染都会重新应用套接字监听器。为了阻止这种情况,一位同事建议我添加一个 bool 值来声明,以防止在重新渲染时重新添加套接字。我更新了组件,结果与第一次没有将消息状态传递到第二个参数时的结果相同 - 它不断替换状态中的第一条消息并且没有附加新消息。我只希望监听器更新本地状态并在新消息通过套接字时重新呈现消息。我有点不知道如何用钩子(Hook)做到这一点。大家有什么想法吗?

我编写的 useEffect Hook 的最终迭代如下。

  useEffect(() => {
    debugger
    if (!isConnected) {
      socket.on('message', (message) => {
        const nextState = messages.slice();
        nextState.push(message);
        setMessage(nextState);
        debugger
      });
      setConnection(true);
    }
  }, [messages, setConnection]);

最佳答案

根据您的代码,我认为只要消息和连接状态发生更改,useEffect 就会运行并进行多个订阅,以在每次渲染时监听每条消息。这是因为只要状态发生变化, react 组件就会渲染。由于 websocket 连接和监听器可以被视为副作用,因此您必须实现返回回调来在组件卸载时处理它们。

假设所有 Websocket 处理和聊天消息逻辑都在单个 React 组件中,您可能只需要像这样在挂载时创建一次 Web 套接字连接和监听器,并提供返回回调来处理卸载(对于场景)就像当您进行页面刷新时,或者您的组件依赖于可能会触发重新渲染的父状态时:

 useEffect(() => {
            socket.connect();
            socket.on('connect', () => {
                console.log('socket connected:', socket.connected);
            });
            socket.on('message',...);
        }
        return () => {
            console.log('websocket unmounting!!!!!');
            socket.off();
            socket.disconnect();

        };
    }, []);

关于reactjs - 为什么 useEffect() 似乎在从套接字接收数据后重置了我的本地状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62207487/

相关文章:

node.js - 如何在 socket.io 的中间件中获取事件详细信息

javascript - 使用参数处理事件处理函数的更好方法是什么?

reactjs - 在 Typescript 项目中实现 react-router PrivateRoute

android - 使用套接字对在两个进程之间通信时,recvmsg 返回错误(EBADF)?

c++ - Google Chrome浏览器在访问网站时发送第二个长度为0的请求吗?

node.js - 为什么我在 Redis 中需要超过 1 个连接,聊天应用程序需要多少内存?

javascript - 如何在React apexcharts中将数据标签移动到中心

javascript - 更改reactJs父组件中的状态并不会更改react子组件中的props

python - 用 Python 编写基于套接字的服务器,推荐策略?

javascript - socket IO 从客户端向服务器发送表单数据