#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]);
Также, если разные комнаты, то возникает проблема с фильтрацией сообщений. Таким образом, чтобы решить эту проблему, есть два возможных способа:
- При отправке данных фильтра с сервера.
- Отфильтруйте данные в клиенте в соответствии с именем пользователя и названием комнаты.
Поскольку это решение не является хорошей практикой, так как каждый раз будет приходить весь список сообщений, и применение фильтра к этому увеличит временную сложность. Но вы можете уменьшить до некоторой степени, создав правильную структуру состояния данных на сервере.
Я думаю, это вам поможет.
Ответ №2:
Вы создаете новое подключение к сокету при каждом эффекте использования, поэтому после десяти сообщений у вас будет десять подключений.
socket = io(END_POINT);
Я сохраняю созданное соединение сокета в useRef, поэтому, если оно создается снова, оно перезаписывается новым и не дублируется.
const socketConection = useRef(null)
useEffect(() => {
socketConnection.current = io(END_POINT)
}, [deps])
Конечно, во всех следующих случаях вы должны использовать socketConection.current
Комментарии:
1. Здравствуйте, Фиде, спасибо за ваш ответ… Я обновил код в соответствии с вашим предложением (и также отредактировал его в вопросе выше), но это не улучшает причину. Можете ли вы предложить какие-то другие возможные решения ?? Еще раз спасибо.
2. Можете ли вы проверить на консоли разработчика, на вкладке Сеть, сколько подключений открыто?
3. И второй эффект использования — вы не должны настраивать нового слушателя для каждого сообщения. длина — прослушиватель должен быть настроен только один раз, при монтировании компонента, с моей точки зрения, в первом эффекте использования.
4. Ты двинулся дальше?
5. Да, сэр, я успешно выполнил его… проблема заключалась в том, что массив сообщений создавался с нуля каждый раз, когда я отправлял сообщение. Следовательно, мне просто пришлось добавить новое сообщение к существующему массиву, что резко сократило количество повторных рендеров и, в свою очередь, решило мою проблему.