You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中useEffect内设置state后Socket监听访问报错问题

问题分析与解决方案

错误原因

首次加载应用时,axios的异步请求还未完成,records状态仍为初始值(通常是空数组)。此时若收到socket推送的新消息,records.find(e=>e.id===msg.conversationId)会返回undefined,后续访问result.Messages就会抛出Cannot read properties of undefined (reading 'Messages')错误。

解决办法

1. 增加空值判断,处理未找到会话的情况

在处理会话记录前,先检查是否找到对应会话,若未找到可直接创建新会话加入列表:

newsocket.on("socketListening", (msg) => {
    console.log(msg)
    try{
        setRecords(prevRecords => {
            const result = prevRecords.find(e=>e.id===msg.conversationId)
            // 未找到对应会话,创建新会话对象
            if(!result){
                const newConversation = {
                    id: msg.conversationId,
                    Messages: [msg],
                    lastMessage: msg.message,
                    Recipient: msg.senderId !== ADMIN.id
                }
                return [newConversation, ...prevRecords]
            }
            // 找到会话,更新消息和会话信息
            const updatedResult = {
                ...result,
                Messages: [msg, ...result.Messages],
                lastMessage: msg.message,
                Recipient: msg.senderId !== ADMIN.id
            }
            return [updatedResult, ...prevRecords.filter(e=>e.id!==msg.conversationId)]
        })
    }catch(e){
        console.log(e)
    }
})

2. 使用函数式更新获取最新状态

因为useEffect依赖为空数组,原回调中的records是初始值的闭包,无法获取到后续更新的状态。用setRecords(prevRecords => { ... })的函数式写法,能直接拿到最新的状态值,避免闭包导致的旧值问题。

3. 避免直接修改原状态对象

原代码中直接修改result.Messages=newMessages会直接篡改状态中的对象,可能导致React无法正确检测状态变化。通过扩展运算符...result创建新对象,保证状态更新的可追踪性。

可选:等待会话列表加载完成后再初始化socket监听

如果业务允许,可以把socket监听逻辑放到axios请求的then回调中,确保只有当会话列表加载完成后才处理socket消息:

useEffect(()=> {
    let newsocket;
    axios
        .get('/api/messages/',{headers:{"auth-token":localStorage.getItem('jwtToken')}})
        .then(res => {
            setRecords(res.data)
            // 会话加载完成后初始化socket
            newsocket = io.connect(CONNECTION_PORT)
            newsocket.on('connect', msg => {
                setSocket(newsocket)
                console.log(`Admin ${ADMIN.id} is waiting. socketID: ${newsocket.id}`)
            });
            newsocket.emit('waiting', ADMIN.id);
            newsocket.on("socketListening", (msg) => {
                setRecords(prevRecords => {
                    const result = prevRecords.find(e=>e.id===msg.conversationId)
                    if(!result){
                        const newConversation = {
                            id: msg.conversationId,
                            Messages: [msg],
                            lastMessage: msg.message,
                            Recipient: msg.senderId !== ADMIN.id
                        }
                        return [newConversation, ...prevRecords]
                    }
                    const updatedResult = {
                        ...result,
                        Messages: [msg, ...result.Messages],
                        lastMessage: msg.message,
                        Recipient: msg.senderId !== ADMIN.id
                    }
                    return [updatedResult, ...prevRecords.filter(e=>e.id!==msg.conversationId)]
                })
            })
        })
        .catch(err =>
            console.log(err)      
        )
    return(()=>{
        if(newsocket) newsocket.close();
    });
},[]);

注意:这种方式可能会丢失axios请求完成前的socket消息,需根据业务场景选择。

内容的提问来源于stack exchange,提问作者kd12345

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 13:30:42