javascript - 在 socket.io 事件上更新组件 - ReactJS,Socket.io
问题描述
我有一个使用 Socket.io 的 React 聊天应用程序。我可以从不同的来源发送消息,但不能更新连接到套接字的每个来源。我试图做到这一点componentDidUpdate
,使用refs
等来保留套接字,但没有任何效果。
插座:
const io = require('socket.io').listen(3002);
const jwt = require('jsonwebtoken');
const messages = [];
io.use((socket, next) => {
if (socket.handshake.query && socket.handshake.query.token) {
jwt.verify(
socket.handshake.query.token,
'4-8-15-16-23-42',
(err, decoded) => {
if (err) return next(new Error('Authentication error'));
socket.decoded = decoded;
next();
}
);
} else {
next(new Error('Authentication error'));
}
}).on('connection', socket => {
socket.emit('connected', {
user: socket.decoded.username
});
socket.on('newMessage', message => {
messages.push(message);
socket.emit('messages', messages);
});
});
客户:
import { useState, useEffect } from 'react';
import io from 'socket.io-client';
const token = sessionStorage.getItem('token');
const socket = io('http://localhost:3002', {
query: { token }
});
const Chat = () => {
const [user, setUser] = useState('');
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
useEffect(() => {
socket.on('connected', msg => {
setUser(msg.user);
});
}, []);
const inputChangeHandler = e => setInput(e.target.value);
const sendMessage = () => {
socket.emit('newMessage', `${user}: ${input}`);
socket.on('messages', newMessages => setMessages(newMessages));
setInput('');
};
return (
<div>
<div id="message-container">
<div>Greetings {user}!!!</div>
{messages.map((message, index) => (
<div key={index}>{message}</div>
))}
</div>
<input value={input} onChange={e => inputChangeHandler(e)} />
<button onClick={sendMessage}>Send</button>
</div>
);
};
export default Chat;
解决方案
您应该在安装组件时连接到套接字,然后附加消息处理程序。
但问题是,如果您在 useEffect 中执行此操作,您的处理程序希望看到状态更改(您的消息数组将始终为空,因为在初始状态下。那是因为 useEffect 有空数组作为第二个参数。这就像鸡蛋问题。你不能使用正常的 useState 或 useReducer 所以......
这是解决方案:
首先删除这个:
socket.on('messages', newMessages => setMessages(newMessages));
来自 sendMessage 函数。
我们需要一些额外的包。
yarn add immer use-immer
import React, {useEffect, useRef } from "react";
import io from 'socket.io-client';
import { useImmer } from "use-immer";
const Chat = () => {
const [messages, setMessages] = useImmer({});
useEffect(() => {
const serverAddress = "https://my-chat-server";
const socket = io( serverAddress,{
transports: ['websocket']
});
socket.on("connect", () => {
console.log("Connected");
socketRef.current = socket;
});
socket.on("message", msg => {
setMessages(msgs => {
return msgs.concat(msg);
});
});
},[setMessages]);
return <div>Here you know what to do :)</div>
}
推荐阅读
- tarantool - 如何自动配置 Tarantool Cartridge 集群?
- ios - 如何像我提供的示例一样创建(集合?)视图
- status - 如何将 Rundeck 作业的步骤状态获取到文件中
- kubernetes - Kubernetes 上带有气流的 Vault
- ruby-on-rails - 如何修复带有链接的渲染提及(例如“@test @test2”)但@test2 链接到@test 的页面
- html - 我可以使用 Microsoft OneDrive 托管网站吗?
- java - 如何检查整个 forEach 列表?(爪哇)
- sql - 通过 HTML 表单更新 SQL 表时出错
- javascript - 检查 url 是否是 Jquery 中的哈希链接
- sql - 对联接查询中的多行求和