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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 19:06:31