在React中如何正确使用socket.on?聊天应用接收消息状态丢失解决方法
问题根源
你遇到的是React的闭包陷阱问题:
- 你把
socket.on的注册逻辑放在了依赖项为空数组的useEffect中,该钩子只会在组件首次挂载时执行1次,回调函数捕获的messages永远是初始值[] - 每次收到新消息调用
setMessages时,都是基于初始空数组扩展,自然会覆盖之前已经更新的消息列表
修复方案
1. 状态更新改为函数式写法
setMessages支持传入回调函数,参数为最新的状态值,不需要依赖外部捕获的messages变量,从根源避免闭包问题。
2. 添加socket事件清理逻辑
组件卸载时要移除socket.on注册的事件回调,否则会出现重复触发、内存泄漏问题。
3. 可选优化:sendMessage方法也改为函数式更新,避免其他场景下的闭包异常
修复后完整代码如下:
export const Room = ({ socket }) => { const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); const { id } = useParams(); useEffect(() => { socket.emit("join room", id); }, [id, socket]); // 补充缺失的依赖项,符合React hooks语法规范 useEffect(() => { const handleMessage = (message) => { // 改为函数式更新,直接读取最新的消息列表 setMessages(prevMessages => [ ...prevMessages, { message: message, createdBy: "other", id: uuidv4() }, ]); }; socket.on("message", handleMessage); // 组件卸载时清理事件监听 return () => { socket.off("message", handleMessage); }; }, [socket]); // 补充依赖项 const sendMessage = () => { console.log("send"); socket.emit("message", input); // 同样改为函数式更新,规避闭包问题 setMessages(prevMessages => [ ...prevMessages, { message: input, createdBy: "me", id: uuidv4() }, ]); // 可选:发送后清空输入框提升使用体验 setInput(""); }; return ( <div className="flex justify-center text-center"> <div className="w-screen h-screen px-2 max-w-4xl mt-10"> <p className="text-3xl">Code: {id}</p> <div className="bg-white w-full h-3/4 rounded-xl border-2 border-black overflow-y-auto"> {messages.map((message) => ( <Message text={message.message} createdBy={message.createdBy} key={message.id} /> ))} </div> <div className="flex flex-row border-2 border-black mt-2 rounded-xl p-2 bg-white"> <input className="flex-grow border-none focus:outline-none" value={input} // 补充value绑定,将输入框改为受控组件避免异常 onChange={(e) => setInput(e.target.value)} // 替换为更通用的onChange事件 /> <button className=" bg-green-500 rounded-xl px-2 py-1" onClick={sendMessage} > Send </button> </div> </div> </div> ); };
内容的提问来源于stack exchange,提问作者Nicke7117
相关产品推荐
相关产品推荐

