React中useState设置后stream状态未更新始终为null如何解决?
React useState更新后在socket回调中获取旧值问题解决方案
问题本质&基础疑问解答
你遇到的是React闭包陷阱问题,和setState是否触发重渲染无关:
- 调用setState后确实会触发组件重新渲染,你的stream状态实际已经更新,可在组件函数体顶部打印stream验证,重渲染时会拿到最新值。
- socket的
new-user事件回调中一直拿到null,是因为useEffect依赖为空数组,只会在组件首次渲染时执行一次,此时绑定的所有socket事件回调捕获的都是首次渲染时的作用域变量,当时stream初始值为null,后续组件重渲染时这些旧的事件回调不会自动更新,因此永远拿不到最新的stream值。
解决方案
方案1:用useRef同步最新stream值(改动最小,推荐)
不需要调整现有socket绑定逻辑,仅新增ref同步state即可:
import { useState, useEffect, useRef } from 'react'; const [stream, setStream] = useState(null); // 新增ref同步最新stream值 const streamRef = useRef(null); async function getLocalStream() { let isFront = true; const devices = await mediaDevices.enumerateDevices(); // 这里改为读ref的值,避免闭包问题 if (!streamRef.current) { let s; try { s = await mediaDevices.getUserMedia({ audio: true, video: true, }); setStream(s); // 更新state的同时同步到ref streamRef.current = s; } catch (e) { console.error(e); } } } useEffect(() => { socket.current = io.connect('http://192.8.1.13:3001'); socket.current.emit('new-room', route.params.roomid, route.params.name); socket.current.on('room-created', async (id, roomid, name) => { getLocalStream(); }); socket.current.on('join-room', async (id, roomid, name) => { getLocalStream(); }); socket.current.on('new-user', async (id, roomid, name) => { // 读ref.current即可拿到最新值 console.log('stream', streamRef.current); }); }, []);
方案2:给useEffect添加stream依赖,同步更新事件绑定
如果不想额外加ref,可在依赖变化时重新绑定事件,需注意在清理函数中解绑旧事件避免重复绑定:
import { useState, useEffect } from 'react'; const [stream, setStream] = useState(null); async function getLocalStream() { let isFront = true; const devices = await mediaDevices.enumerateDevices(); if (!stream) { let s; try { s = await mediaDevices.getUserMedia({ audio: true, video: true, }); setStream(s); } catch (e) { console.error(e); } } } useEffect(() => { socket.current = io.connect('http://192.8.1.13:3001'); socket.current.emit('new-room', route.params.roomid, route.params.name); // 单独定义事件回调方便后续解绑 const handleRoomCreated = async (id, roomid, name) => { getLocalStream(); }; const handleJoinRoom = async (id, roomid, name) => { getLocalStream(); }; const handleNewUser = async (id, roomid, name) => { console.log('stream', stream); }; socket.current.on('room-created', handleRoomCreated); socket.current.on('join-room', handleJoinRoom); socket.current.on('new-user', handleNewUser); // 清理函数:依赖变化/组件卸载时解绑旧事件、断开连接 return () => { socket.current.off('room-created', handleRoomCreated); socket.current.off('join-room', handleJoinRoom); socket.current.off('new-user', handleNewUser); socket.current.disconnect(); }; // 把stream加入依赖数组,stream变化时重新执行effect更新回调 }, [stream]);
方案选择建议
如果socket事件较多或后续有其他依赖需要新增,第一种用ref的方案更简洁,不会频繁解绑重绑事件,性能更好。
内容的提问来源于stack exchange,提问作者nishanth nish
相关产品推荐
相关产品推荐

