首页 > 解决方案 > 带有 React 的 Socket.io 在 5 条消息后表现异常

问题描述

我正在尝试使用 socket.io 构建一个基本的聊天应用程序并做出反应,但遇到了一个奇怪的问题。该应用程序的工作方式与前 5 条消息的预期相同,之后第 6 条消息加载时间过长,并且通常之前的一些消息不会显示在聊天框中。如果有人可以提供帮助会很高兴。这是我的后端代码:

const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);

io.on('connection', socket => {
  socket.on('message', ({ name, message }) => {
    io.emit('message', { name, message });
    console.log(message);
    console.log(name);
  });
});

http.listen(4000, function () {
  console.log('listening on port 4000');
});

这是我在 App.js 中的代码:

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

const App = props => {
  const socket = io.connect('http://localhost:4000');
  const [details, setDetails] = useState({ name: '', message: '' });
  const [chat, setChat] = useState([]);


  useEffect(() => {
    socket.on('message', ({ name, message }) => {
      setChat([...chat, { name, message }]); //same as {name:name,message:message}
    });
  });

  const onMessageSubmit = e => {
    e.preventDefault();
    const { name, message } = details;
    socket.emit('message', { name, message });
    setDetails({ name, message: '' });
  };

  return (
    <div>
      <form onSubmit={onMessageSubmit}>
        <input
          type='text'
          value={details.name}
          onChange={e => setDetails({ ...details, name: e.target.value })}
        />
        <input
          type='text'
          value={details.message}
          onChange={e => setDetails({ ...details, message: e.target.value })}
        />
        <button>Send</button>
      </form>
      <ul>
        {chat &&
          chat.map((chat, index) => (
            <li key={index}>
              {chat.name}:{chat.message}
            </li>
          ))}
      </ul>
    </div>
  );
};

export default App;

标签: node.jsreactjsexpresssockets

解决方案


那是因为每次你更新状态,useEffect 回调运行,基本上你message一次又一次地订阅。

在几次迭代之后,您有多个订阅尝试更新相同的状态。而且由于 的setState​​异步性质,您会看到奇怪的行为。

你只需要订阅一次,你可以通过传递空依赖参数来做到这一点,useEffect这将使它像componentDidMount

 useEffect(() => {
    socket.on('message', ({ name, message }) => {
      setChat([...chat, { name, message }]);
    });
  }, []);

编辑 - 要处理异步并考虑之前的聊天,您需要setState通过回调

useEffect(() => {
  socket.on("message", ({ name, message }) => {
    setChat((prevChat) => prevChat.concat([{ name, message }]));
  });
}, []);

cleanup当您的组件卸载时,您可能希望这样做。请查看官方文档


推荐阅读