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

React Native中useState更新后函数内值未变更问题排查

React Native WebSocket回调读取useState旧值问题解析

问题代码

const [id, setId] = useState(null);

const functionOne = (id) => {
    setId(id);
};

const functionTwo = (newId) => {
    if (id === newId) {
        console.log('ids are the same');
    } else {
        console.log('ids are not the same');
    }
};

useEffect(() => {
    const socket = new WebSocket(url);

    socket.addEventListener('open', () => {
        console.log('socket connection opened');
    });

    socket.addEventListener('message', (msg) => {
        const data = JSON.parse(msg.data);

        if (data.eventType === 'one') {
            functionOne(data.id);
        }

        if (data.eventType === 'two') {
            functionTwo(data.id);
        }
    }) 

    socket.addEventListener('close', () => {
        console.log('socket connection closed');
    });
}, []);

场景与问题

通过useState存储ID,服务端通过WebSocket发送两种事件:

  • 事件one:将收到的ID存入useState,已验证状态更新成功
  • 事件two:一分钟后发送,携带与one相同的ID,需要和存储的ID比对

问题:调用functionTwo时,id始终是初始值null,即使状态已更新;用useCallback包裹functionTwo并传入[id]依赖也无法解决。

问题根源

这是典型的闭包陷阱:

  • useEffect的依赖数组为空,仅在组件首次渲染时创建WebSocket连接,其内部的message回调捕获的是首次渲染时的functionOne和functionTwo
  • 当setId更新状态触发组件重新渲染时,会生成新的functionOne和functionTwo(引用最新的id),但WebSocket连接仍使用旧的回调函数,调用的是旧版本的functionTwo,自然读取的是初始的null
  • 仅用useCallback包裹functionTwo但未更新WebSocket回调,旧回调依然引用旧的functionTwo,因此无效

解决方法

方法一:更新useEffect依赖并重建WebSocket

通过将id、functionOne、functionTwo加入useEffect依赖数组,确保状态或函数更新时重建WebSocket,同时用useCallback稳定函数引用:

const [id, setId] = useState(null);

const functionOne = useCallback((id) => {
    setId(id);
}, []);

const functionTwo = useCallback((newId) => {
    if (id === newId) {
        console.log('ids are the same');
    } else {
        console.log('ids are not the same');
    }
}, [id]);

useEffect(() => {
    const socket = new WebSocket(url);

    socket.addEventListener('open', () => {
        console.log('socket connection opened');
    });

    socket.addEventListener('message', (msg) => {
        const data = JSON.parse(msg.data);

        if (data.eventType === 'one') {
            functionOne(data.id);
        }

        if (data.eventType === 'two') {
            functionTwo(data.id);
        }
    }) 

    socket.addEventListener('close', () => {
        console.log('socket connection closed');
    });

    // 组件卸载或依赖变化时关闭旧连接
    return () => {
        socket.close();
    };
}, [id, functionOne, functionTwo]);

方法二:用useRef保存最新状态

通过useRef存储id的最新值,避免闭包捕获旧值:

const [id, setId] = useState(null);
const idRef = useRef(id);

// 状态更新时同步更新ref
useEffect(() => {
    idRef.current = id;
}, [id]);

const functionOne = (id) => {
    setId(id);
};

const functionTwo = (newId) => {
    // 读取ref的current属性获取最新id
    if (idRef.current === newId) {
        console.log('ids are the same');
    } else {
        console.log('ids are not the same');
    }
};

useEffect(() => {
    const socket = new WebSocket(url);

    // ... 事件监听代码(无需修改)
}, []);

方法三:全局状态管理(如Redux)

将id存储到全局状态(如Redux)中,函数直接从全局获取最新值,不受组件渲染闭包限制,这也是用户最终采用的方案:通过自定义Hook模拟useState,将值存入Redux后问题解决。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 01:42:44