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

React-Vite-TS项目中useEffect内Three.js初始化函数执行两次问题

问题原因

这是Vite+React开发环境下的**React严格模式(Strict Mode)**导致的。React在开发模式下的Strict Mode会刻意重复执行一些生命周期函数和副作用(包括空依赖的useEffect),目的是帮你提前检测代码中可能存在的副作用泄漏问题——比如未清理的定时器、重复订阅等。而生产环境(npm run build后)Strict Mode会自动禁用,所以不会出现重复调用的情况。

修复方案

方法1:临时关闭React Strict Mode

在项目入口文件(通常是src/main.tsx)中,找到包裹App的<StrictMode>标签并移除,开发环境下就不会重复执行useEffect了:

// 移除前
ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
);

// 移除后
ReactDOM.createRoot(document.getElementById('root')!).render(
  <App />,
);

方法2:给Three.js初始化逻辑加防重复判断

如果不想关闭Strict Mode(毕竟它能帮你排查潜在问题),可以在initThreeProcess函数里加一层判断,确保渲染器DOM元素只被插入一次:

function initThreeProcess(container: HTMLElement) {
  // 检查容器内是否已有渲染器元素,有则直接返回
  if (container.querySelector('.webgl-renderer')) return;

  // 你的Three.js初始化逻辑
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.domElement.classList.add('webgl-renderer'); // 标记类名用于判断
  container.appendChild(renderer.domElement);

  // VR按钮、3D物体添加等后续逻辑...
}

方法3:用useRef标记初始化状态

在组件内通过useRef记录是否已经完成初始化,避免useEffect重复执行逻辑:

import { useEffect, useRef } from 'react';

const ThreeCanvasContainer = () => {
  const containerRef = useRef<HTMLDivElement>(null);
  const hasInitialized = useRef(false);

  useEffect(() => {
    if (!containerRef.current || hasInitialized.current) return;
    
    initThreeProcess(containerRef.current);
    hasInitialized.current = true;
  }, []);

  return <div ref={containerRef}></div>;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 08:52:12