試圖讓一個基本的聊天應用程式運行,并且在發送訊息時遇到過度重新呈現的問題。這是客戶端的適用代碼:
const [chatMessages, setChatMessages] = useState([]);
const sendChat = (e) => {
socket.emit("sendMessage", e.target.elements.message.value);
}
useEffect(() => {
socket.on("receiveMessage", (chatMessage) => {
setChatMessages([...chatMessages, chatMessage]);
console.log(chatMessages);
});
}, [chatMessages]);
return (
{chatMessages.map((message) => <p>{message}</p>)}
)
然后,在服務器上:
io.on("connection", (socket) => {
socket.on("sendMessage", (chatMessage) => {
console.log("message sent");
io.to(roomId).emit("receiveMessage", chatMessage);
});
}
當我這樣做時,訊息已成功發送和接收,但會導致它發生很多次(控制臺):
[]
[]
[{...}]
[{...}]
(2) [{...}, {...}]
在第三條訊息中,這是記錄的內容。到第六條或第七條訊息時,整個頁面在記錄大約 100 次時就停止了。
我嘗試了以下方法:
在
useEffect(). 這確實修復了重新渲染,但引入了一個新問題。最新訊息是唯一保存并替換最后一條訊息的訊息,因此您一次只能看到一條訊息。一起把它拿出來
useEffect()。這只會使問題惡化,并導致每條訊息的重新渲染次數更多。
Any help would be appreciated. Thank you!
uj5u.com熱心網友回復:
問題
chatMessages當狀態更新但不清理它們時,您正在創建套接字事件處理程式。如果您編輯您的代碼或組件重新渲染等......然后添加另一個套接字事件處理程式。多個處理程式將開始堆疊并將多個意外狀態更新排入佇列。
此外,由于 React 狀態更新是異步處理的,因此您無法在將更新加入佇列后立即記錄狀態并期望看到更新后的狀態。為此使用單獨的useEffect鉤子。
解決方案
添加清理函式以洗掉事件處理程式,并將更新后的狀態陣列
useEffect重新包含在處理程式回呼中。chatMessagesuseEffect(() => { const handler = (chatMessage) => { setChatMessages([...chatMessages, chatMessage]); } socket.on("receiveMessage", handler); return () => socket.off("receiveMessage", handler); }, [chatMessages]);添加一個
useEffect清理函式,洗掉依賴項,以便效果在組件掛載時運行一次,并使用功能狀態更新來正確更新之前的狀態,而不是回呼外殼中的初始狀態。useEffect(() => { const handler = (chatMessage) => { setChatMessages(chatMessages => [...chatMessages, chatMessage]); } socket.on("receiveMessage", handler); return () => socket.off("receiveMessage", handler); }, []);
在兩者之間,第二個選項是更優的解決方案,但您選擇哪個是您的決定。
要記錄chatMessages狀態更新:
useEffect(() => {
console.log(chatMessages);
}, [chatMessages]);
uj5u.com熱心網友回復:
由于您依賴于chatMessages,因此每次chatMessages更改時,它都會創建一個新的偵聽器。這就是為什么當更多訊息進來時它變得越來越慢。
你可以做兩件事:
- 您在方法內維護
chatMessages本地useEffect。您可以擴展該陣列并setChatMessages使用擴展陣列進行呼叫。執行此操作時,您可以洗掉對的chatMessages依賴項useEffect并仍然擁有所有訊息。作為一種好的做法,您應該回傳一個函式,該函式將在組件卸載時洗掉事件偵聽器。
const [chatMessages, setChatMessages] = useState([]);
const sendChat = (e) => {
socket.emit("sendMessage", e.target.elements.message.value);
}
useEffect(() => {
let localMessages = [];
const callback = (chatMessage) => {
localMessages = [...localMessages, chatMessage];
setChatMessages(localMessages);
console.log(localMessages);
};
socket.on("receiveMessage", callback);
return () => {
socket.off("receiveMessage", callback);
}
}, []);
return (
{chatMessages.map((message) => <p>{message}</p>)}
)
- 您可能可以
useRef用于存盤值。但是,當值更改時,這不會觸發 UI 的重新呈現,并且可能這不是您想要的。所以這可能不是一個好的選擇。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/434929.html
標籤:javascript reactjs sockets react-hooks socket.io
上一篇:讀取呼叫會使執行速度減慢1分鐘?
下一篇:C/C 通過套接字下載/上傳檔案
