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

WebRTC+Socket.io视频通话:React状态Local Stream在Socket事件后为null

问题分析与解决方案

核心问题

你遇到的问题是闭包导致的旧状态捕获:

  • handleCallAccepted通过useCallback创建,依赖项为空数组,因此它捕获的是组件初始渲染时的state(此时stream为null)。
  • 后续turnOnYourVideo成功获取媒体流并更新state后,handleCallAccepted并未重新创建,所以当socket触发video:call:accepted事件时,回调内访问的仍是初始的null值。

解决方案

方法1:更新useCallback的依赖数组

将state加入useCallback的依赖列表,确保回调始终能访问最新的状态。同时更新监听socket的useEffect依赖,保证回调更新时重新绑定事件:

const handleCallAccepted = useCallback(({ from, ans }) => {
    console.log('from', from);
    console.log('ans', ans);
    console.log('handleCallAccepted', state?.stream);
    // 获取LocalStream的tracks
    if (state?.stream) {
        const tracks = state.stream.getTracks();
        console.log('Local Stream Tracks', tracks);
    }
}, [state]); // 添加state到依赖

useEffect(() => {
    socket.on('video:call:accepted', handleCallAccepted);
    return () => {
        socket.off('video:call:accepted', handleCallAccepted);
    }
}, [handleCallAccepted]); // 添加handleCallAccepted到依赖

方法2:使用useRef保存媒体流引用

如果不想频繁创建回调,可以用useRef存储媒体流的可变引用,避免闭包问题:

const WebRTC = () => {
    const [state, dispatch] = useReducer(reducer, initialState);
    const streamRef = useRef(null); // 创建ref存储媒体流

    const turnOnYourVideo = async () => {
        const constraints = {
            audio: true,
            video: true
        }
        const localStream = await navigator.mediaDevices.getUserMedia(constraints);
        dispatch({ type: 'stream', stream: localStream });
        streamRef.current = localStream; // 更新ref的current值
    }

    const handleCallAccepted = useCallback(({ from, ans }) => {
        console.log('from', from);
        console.log('ans', ans);
        console.log('handleCallAccepted', streamRef.current);
        // 获取LocalStream的tracks
        if (streamRef.current) {
            const tracks = streamRef.current.getTracks();
            console.log('Local Stream Tracks', tracks);
        }
    }, []); // 依赖为空,ref的current是可变值不受闭包影响

    // 其余代码不变...
};

额外注意

你代码中存在事件名拼写错误:video:call:accepteed应该修正为video:call:accepted,需要统一修正两端的事件名,避免事件无法正常触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 01:37:05