React集成Three.js报错:Cannot read properties of null (reading 'width')
解决React中Three.js获取Canvas为null的问题
问题根源
你用setTimeout和全局DOM查询获取Canvas的方式不可靠:React组件渲染是异步的,即便等3秒,也无法保证Canvas已经挂载到DOM;如果页面存在多个同class的Canvas,选择器还会拿到错误元素。
修复方案
用React官方的useRef和useEffect安全获取Canvas并初始化Three.js:
- 修改React组件,用Ref绑定Canvas
import { useRef, useEffect } from 'react'; import { displayObject } from "../main" export default function Atraction() { const canvasRef = useRef(null); useEffect(() => { // 确认Canvas元素已挂载到DOM if (!canvasRef.current) return; // 把Canvas元素直接传入初始化函数 displayObject(canvasRef.current, myArgs); }, []); // 空依赖数组确保代码仅在组件挂载完成后执行一次 return ( <canvas ref={canvasRef} className="webgl-1"></canvas> ) }
- 调整
displayObject函数,接收Canvas参数
把原来在displayObject里用document.querySelector获取Canvas的逻辑,替换为直接使用传入的元素:
import * as THREE from 'three'; export function displayObject(canvas, args) { // 用传入的Canvas初始化渲染器 const renderer = new THREE.WebGLRenderer({ canvas }); // 基于Canvas实际尺寸设置渲染器大小 renderer.setSize(canvas.clientWidth, canvas.clientHeight); // 后续场景创建、模型加载等逻辑 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 1000); // 模型加载代码... }
额外注意事项
- 如果Canvas尺寸会动态变化(比如窗口 resize),要在
useEffect中添加监听事件,同步更新渲染器尺寸。 - 组件卸载时,要在
useEffect的返回函数中清理Three.js资源(比如执行renderer.dispose()、取消动画帧),避免内存泄漏。
内容的提问来源于stack exchange,提问作者Alin-Alexandru Peter
相关产品推荐
相关产品推荐

