在React中,我使用useState
定义了一个数组变量,名为messages
。我在页面上也有一个组件,它没有任何DOM,而是侦听WebSocket上的事件。当我的组件收到来自WebSocket的事件通知时,我的messages
变量的长度总是零-尽管它之前有多个消息。为什么在输入函数时数组是空的?
下面是重现该问题的最小代码集:WebSocketNotificationHandler.tsx
:
import { useEffect } from "react";
import config from "../../../config.json";
import { IChatMessage } from "../../../services/chatService";
interface WebSocketNotificationHandlerProps {
onReceiveMessages: (newMessages: IChatMessage[]) => void;
}
export let client: WebSocket = new WebSocket(
config.webSocketAddress,
config.webSocketProtocol
);
const WebSocketNotificationHandler = (
props: WebSocketNotificationHandlerProps
) => {
useEffect(() => {
if (client) {
client.onmessage = async (ev) => {
handleWebSocketMessage(ev);
}
}
}, []);
const handleWebSocketMessage = async (ev: MessageEvent<string>) => {
props.onReceiveMessages([]); // Simplified, normally we send one or more
}
return (
<></>
);
}
export default WebSocketNotificationHandler;
字符串GroupPage.tsx
:
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import chatService, { IChatMessage, ICreateMessageResponse } from '../../../services/chatService';
import ChatMessage from '../../atoms/ChatMessage/ChatMessage';
import ChatStagingArea from '../../organisms/ChatStagingArea/ChatStagingArea';
import WebSocketNotificationHandler from '../../organisms/WebSocketNotificationHandler/WebSocketNotificationHandler';
const GroupPage = () => {
const { groupName } = useParams();
const [messages, setMessages] = useState<IChatMessage[]>([]);
const loadGroup = async () => {
setMessages([]);
await chatService // REST API to get current messages, returns a list >0 elements
.getGroup(groupName)
.then(data => {
setMessages([...data.data.messages]);
});
}
useEffect(() => {
loadGroup();
}, []);
const handleMessageSent = (message: ICreateMessageResponse, text: string) => {
let newMessages = [...messages, { /* Message data for a new message */ }];
setMessages(newMessages);
}
const handleNewMessages = (newMessages: IChatMessage[]) => {
console.log(messages.length); // Always 0 even if I can see multiple???
// Code goes here that adds the newMessages to the messages array
};
return (
<>
{messages && messages.map(m => <ChatMessage key={m.id} author={m.fullName} date={m.authoredDate} message={m.message} />)}
<ChatStagingArea groupName={groupName} onSent={(message, text) => handleMessageSent(message, text)} />
<WebSocketNotificationHandler onReceiveMessages={newMessages => handleNewMessages(newMessages)} />
</>
);
}
export default GroupPage;
型
3条答案
按热度按时间iugsix8n1#
问题
问题是
handleNewMessages
回调中的messages
状态上的陈旧闭包,该回调在传递给WebSocketNotificationHandler
的匿名函数中被调用为onReceiveMessages
,其中它由实际示例化client.onmessage
事件处理程序的handleWebSocketMessage
调用。初始为空的
messages
状态数组是在事件处理程序和回调被示例化时从初始呈现周期开始在作用域中关闭的对象。溶液
如果您只是想附加发送的消息和接收的消息数组,那么您在
GroupPage
中的处理程序应该真正使用功能状态更新。函数状态更新将回调函数传递给状态更新器函数,状态更新器函数将传递当前状态值,然后计算并返回下一个状态值。这避免了在您尝试更新的状态值上出现陈旧闭包的问题。示例如下:
字符串
如果你想控制台记录
messages
状态数组长度,那么使用一个单独的useEffect
钩子来实现:型
如果你 * 真的 * 必须在
handleNewMessages
中记录messages
状态数组长度,那么使用React ref来缓存当前的messages
状态值,以便在组件生命周期中的任何时候都可以访问ref的当前值。示例如下:
型
2ekbmq322#
根据我的理解,当执行
handleWebSocketMessage
函数时,它指向消息的初始状态,该状态是一个空数组,即使父GroupPage
组件中的状态已更改。您可以使用
useCallback
钩子来创建handleNewMessages
函数的备忘录版本,这将确保该函数始终引用setMessages
函数的最新版本。字符串
这同样适用于
grouppage
组件。型
1bqhqjot3#
WebSocketNotificationHandler.tsx:
字符串
GroupPage.tsx:
型
这些更改确保WebSocket事件得到正确处理,并且在新消息到达时正确更新消息状态。清理WebSocket侦听器也是为了防止内存泄漏。