React StrictMode引发实例创建问题时的解决方案抉择
React StrictMode 双渲染导致异步连接重复创建的问题解决
问题分析
React StrictMode在开发环境下会触发组件的两次挂载/卸载,目的是提前暴露潜在的副作用问题。你的场景中,由于WebRTC连接的初始化是异步的,第一次卸载时rtc对象还未完成初始化,此时全局rtc被置为null;第二次挂载时又会重新创建新的连接,最终导致重复的WebSocket连接。
核心问题点:
- 使用全局变量
rtc管理连接,容易在组件重复渲染/挂载时产生竞态 - 异步初始化流程未考虑组件卸载后的中断处理
你的代码示例
let rtc = null; const init = async () => { if (!rtc) { rtc = new WebRTCConnection(); await rtc.init("ws://localhost:8080"); document.getElementById("studentPeer").srcObject = rtc.localStream; document.getElementById("vcsPositionPeer").srcObject = rtc.remoteStream; } }; const Recognition = () => { useEffect(() => { // Anything in here is fired on component mount. init(); return () => { // Anything in here is fired on component unmount. if (rtc) rtc.release(); rtc=null; }; }, []); return (...); };
解决方案
1. 保留StrictMode,不要移除
StrictMode是开发环境的重要调试工具,线上环境不会触发双渲染,它能帮你提前发现类似的副作用泄漏问题,因此强烈建议保留。
2. 不要强制改为同步初始化
异步初始化WebRTC/WebSocket是合理的设计,没必要为适配StrictMode放弃异步,只需优化异步流程的控制逻辑即可。
具体修复代码
将全局变量改为组件内部的useRef,并添加挂载状态标记处理异步竞态:
import { useEffect, useRef } from 'react'; const Recognition = () => { // 用组件内部Ref存储连接实例,避免全局污染 const rtcRef = useRef(null); // 标记组件是否处于挂载状态,用于异步任务的中断判断 const isMountedRef = useRef(true); useEffect(() => { const initConnection = async () => { // 如果已有连接,直接返回 if (rtcRef.current) return; const newRtc = new WebRTCConnection(); try { await newRtc.init("ws://localhost:8080"); // 只有组件还挂载时,才保存实例并操作DOM if (isMountedRef.current) { rtcRef.current = newRtc; document.getElementById("studentPeer").srcObject = newRtc.localStream; document.getElementById("vcsPositionPeer").srcObject = newRtc.remoteStream; } else { // 组件已卸载,直接释放刚创建的连接 newRtc.release(); } } catch (err) { console.error('连接初始化失败:', err); // 初始化失败也要清理资源 newRtc.release(); } }; initConnection(); // 卸载时的清理逻辑 return () => { isMountedRef.current = false; if (rtcRef.current) { rtcRef.current.release(); rtcRef.current = null; } }; }, []); return (...); };
修复逻辑说明
- 组件内部Ref替代全局变量:每个组件实例拥有独立的连接实例,避免StrictMode双渲染或组件多次挂载时的状态冲突。
- 挂载状态标记:异步初始化完成后,先检查组件是否还处于挂载状态,避免在组件已卸载时执行DOM操作或保存无效实例。
- 完整的资源清理:无论初始化是否成功,都确保连接资源被正确释放;卸载时立即标记组件为未挂载,阻止后续异步任务的无效执行。
内容的提问来源于stack exchange,提问作者peterphonic
相关产品推荐
相关产品推荐

