Браузеры зависают при использовании сокета.ввод-вывод с помощью ReactJS и ExpressJS

#node.js #reactjs #express #socket.io

Вопрос:

Проблема:

Я работаю над приложением для чата. Когда я отправляю более 9-10 запросов, браузер замедляется и в конце концов просто зависает. При обновлении страницы все возвращается в нормальное состояние. Я поискал в розетке.документация ввода-вывода, но не смогла получить никакого решения по этому вопросу.

Код:

Вот мой Бэкенд-экспресс.Код JS:

index.js

 const express = require("express");
const { addUser, getUser, deleteUser, getAllUsers } = require("./users_api");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server, {
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
  },
});

const port = 5000 || process.env.PORT;

io.on("connection", (socket) => {
  socket.on("join", ({ name, room }, callback) => {
    const { err, user } = addUser({ id: socket.id, name, room });
    if (err) return callback(err);
    if (user) {
      socket.emit("message", {
        user: "admin",
        text: `${user.name} has entered the room ${user.room}.`,
      });
      socket.broadcast.to(user.room).emit("message", {
        user: "admin",
        text: `${user.name} has joined the room.`,
      });
      socket.join(user.room);
      io.to(user.room).emit("users", {
        room: user.room,
        users: getAllUsers(user.room),
      });

      callback();
    }
  });

  socket.on("client_message", (msg) => {
    const user = getUser(socket.id);
    if (user) io.to(user.room).emit("message", { user: user.name, text: msg });
  });

  socket.on("disconnect", () => {
    console.log(6);
    const user = deleteUser(socket.id);
    if (user) {
      io.to(user.room).emit("message", {
        user: "admin",
        text: `${user.name} has left the room.`,
      });
      io.to(user.room).emit("users", {
        room: user.room,
        users: getAllUsers(user.room),
      });
    }
  });
});

server.listen(port, () => console.log(`Server started at port ${port}.`));
 

users_api.js:

 const current_users = [];

const addUser = ({ id, name, room }) => {
  name = name.trim().toLowerCase().split(" ").join("_");
  room = room.trim().toLowerCase();

  const exist_user = current_users.find(
    (user) => name === user.name amp;amp; room === user.room
  );

  if (exist_user)
    return {
      err: "User with this name already exists.",
    };

  current_users.push({ id, name, room });
  return { user: { id, name, room } };
};

const deleteUser = (id) => {
  const index = current_users.findIndex((user) => user.id === id);
  if (index !== -1) return current_users.splice(index, 1)[0];
};

const getUser = (id) => current_users.find((user) => user.id === id);

const getAllUsers = (room) =>
  current_users.filter((user) => user.room === room);

module.exports = { addUser, deleteUser, getUser, getAllUsers };
 

Интерфейсный код:

 import io from "socket.io-client";
import React, { useEffect, useRef, useState } from "react";
import queryString from "query-string";
import "./ChatPage.css";

const END_POINT = "http://localhost:5000";

const ChatPage = (props) => {
  const [message, setMessage] = useState("");
  const [messages, setMessages] = useState([]);
  const [users, setUsers] = useState([]);
  const socket = useRef(null);

  const handleSubmit = () => {
    socket.current.emit("client_message", message);
    setMessage("");
  };

  useEffect(() => {
    const { name, room } = queryString.parse(props.location.search);
    socket.current = io(END_POINT);
    socket.current.emit("join", { name, room }, (error) => {
      if (error) alert(error);
    });

    return () => {
      socket.current.disconnect();
      socket.current.off();
    };
  }, [props.location.search, END_POINT]);

  useEffect(() => {
    socket.current.on("message", (msg) => {
      setMessages([...messages, msg]);
    });
    socket.current.on("users", ({ users }) => {
      setUsers(users);
    });
  }, [messages.length]);

  return (
    <div className="chat-room-container">
      <div className="chat-room-left">
        <ul>
          {messages.map((message, i) => (
            <li key={i}>
              {message.name}
              {message.text}
            </li>
          ))}
        </ul>
        <input
          type="text"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
        />
        <button type="button" onClick={handleSubmit}>
          Submit
        </button>
      </div>
      <div className="chat-room-right">
        <ul>
          {users.map((user, i) => (
            <li key={i}>{user.name}</li>
          ))}
        </ul>
      </div>
    </div>
  );
};

export default ChatPage;
 

Я не понимаю, в чем дело, пожалуйста, помогите.

Ответ №1:

Вместо отправки сообщения о событии или трансляции попробуйте отправить список сообщений конкретному пользователю или список всех пользователей с их сообщениями, принадлежащими одной комнате. Это приведет к меньшему количеству рендеринга во внешнем интерфейсе.

Например, Ваш код интерфейса изменится следующим образом,

 useEffect(() => {
    socket.current.on("message", (msgList) => {
      setMessages(msgList);
    });
    socket.current.on("users", ({ users }) => {
      setUsers(users);
    });
  }, [messages.length]);
 

Также, если разные комнаты, то возникает проблема с фильтрацией сообщений. Таким образом, чтобы решить эту проблему, есть два возможных способа:

  1. При отправке данных фильтра с сервера.
  2. Отфильтруйте данные в клиенте в соответствии с именем пользователя и названием комнаты.

Поскольку это решение не является хорошей практикой, так как каждый раз будет приходить весь список сообщений, и применение фильтра к этому увеличит временную сложность. Но вы можете уменьшить до некоторой степени, создав правильную структуру состояния данных на сервере.

Я думаю, это вам поможет.

Ответ №2:

Вы создаете новое подключение к сокету при каждом эффекте использования, поэтому после десяти сообщений у вас будет десять подключений.

 socket = io(END_POINT);
 

Я сохраняю созданное соединение сокета в useRef, поэтому, если оно создается снова, оно перезаписывается новым и не дублируется.

 const socketConection = useRef(null)

useEffect(() => {
  socketConnection.current = io(END_POINT)
}, [deps])
 

Конечно, во всех следующих случаях вы должны использовать socketConection.current

Комментарии:

1. Здравствуйте, Фиде, спасибо за ваш ответ… Я обновил код в соответствии с вашим предложением (и также отредактировал его в вопросе выше), но это не улучшает причину. Можете ли вы предложить какие-то другие возможные решения ?? Еще раз спасибо.

2. Можете ли вы проверить на консоли разработчика, на вкладке Сеть, сколько подключений открыто?

3. И второй эффект использования — вы не должны настраивать нового слушателя для каждого сообщения. длина — прослушиватель должен быть настроен только один раз, при монтировании компонента, с моей точки зрения, в первом эффекте использования.

4. Ты двинулся дальше?

5. Да, сэр, я успешно выполнил его… проблема заключалась в том, что массив сообщений создавался с нуля каждый раз, когда я отправлял сообщение. Следовательно, мне просто пришлось добавить новое сообщение к существующему массиву, что резко сократило количество повторных рендеров и, в свою очередь, решило мою проблему.