我有一个简单的反应组件,它通过 SocketIO 发送和接收消息。我的状态钩子看起来像这样
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 钩子中。这个想法是使用钩子代替 componentDidMount,所以我最初通过传递一个空数组作为第二个参数来使用 Effect。当我这样做时,每当收到消息时,它似乎都会清除我的"消息"状态,然后用新消息替换内容。从浏览器来看,似乎一次只能保持一条消息的状态 - 上一条消息不断被替换。我厌倦了在第二个参数数组中传递"消息"状态,现在消息似乎被附加到状态中,并且组件在收到时重新渲染 - 很酷吧,这就是我想要的?
我注意到我在每条后续消息上都遇到了性能问题,最终发现套接字侦听器在每次渲染时都会重新应用。为了阻止这种情况,一位同事建议我在状态中添加一个布尔值,以防止在重新渲染时重新添加套接字。我更新了组件,结果与第一次没有将消息状态传递到第二个参数时相同 - 它不断替换状态中的第一条消息并且没有附加新消息。我只希望侦听器更新本地状态,并在新消息通过套接字时重新呈现消息。我对如何使用钩子做到这一点有点不知所措。有人有什么想法吗?
我编写的useEffect钩子的最终迭代如下。
useEffect(() => {
debugger
if (!isConnected) {
socket.on('message', (message) => {
const nextState = messages.slice();
nextState.push(message);
setMessage(nextState);
debugger
});
setConnection(true);
}
}, [messages, setConnection]);
从您的代码中,我认为 useEffect 将运行并创建多个订阅,以便在消息和连接状态发生变化时侦听每条消息、每个渲染。这是因为每当状态发生变化时,反应组件都会呈现。由于 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();
};
}, []);