React集成Three.js时出现读取null的addEventListener/appendChild报错如何解决
你遇到的两个报错本质都是Three.js实例化时机早于React组件DOM渲染时机导致的:
ImageDistortion.js末尾直接写了new WebGL(),这段代码会在你Hero.js导入这个文件的时候立刻执行,此时Hero组件的.hero容器、h1、.three--anim类的元素都还没渲染到页面上,所有document.querySelector查询结果都是null,才会触发读取null属性的报错。你之前加的非空判断只是绕过了第一个报错,本质问题没解决,才会继续报appendChild的错。
修复步骤
第一步:修改ImageDistortion.js
- 删除文件末尾的
new WebGL()这行代码,改为导出WebGL类 - 调整类的构造函数,支持从外部传入DOM元素,避免硬编码全局DOM查询,改完的核心部分如下:
// 前面的import、lerp函数、纹理加载逻辑都保留 export default class WebGL{ // 构造函数接收容器元素、hover触发元素、动画链接元素三个参数 constructor(container, hoverElement, animLinks){ this.container = container; // 不用再querySelector,直接用传入的DOM this.links = animLinks; // 后面原有逻辑保留... // 把原来的this.addEventListeners(document.querySelector('h1'))改成用传入的hoverElement this.addEventListeners(hoverElement); // 剩下的setUpCamera、createMesh等原有逻辑都保留 } // 后面的类方法都保留,可新增销毁方法避免内存泄漏: destroy(){ window.removeEventListener('resize', this.onWindowResize.bind(this)) this.renderer.dispose() this.container.removeChild(this.renderer.domElement) } }
第二步:修改Hero.js,用React钩子控制实例化时机
需要用useRef获取真实DOM元素,用useEffect保证组件挂载完成后再实例化WebGL:
import React, {useEffect, useRef} from 'react' import WebGL from './utils/ImageDistortion' const Hero = () => { // 定义ref获取DOM元素 const heroRef = useRef(null) const hoverRef = useRef(null) useEffect(()=>{ // 只有当DOM都挂载完成后才执行 if(heroRef.current && hoverRef.current){ const animLinks = [...hoverRef.current.querySelectorAll('.three--anim')] // 实例化WebGL,传入对应DOM const webglInstance = new WebGL(heroRef.current, hoverRef.current, animLinks) // 组件卸载时销毁实例,避免内存泄漏 return ()=>{ webglInstance.destroy() } } }, []) return ( <div className="hero" ref={heroRef}> <p>Hello.</p> <h1 id="three--anim--container" ref={hoverRef}> I'm <span className="name three--anim">Joris Delorme</span>, based in <span className="location three--anim">Lyon,France</span>.<br /> I'm minimalist designer, developer and photographer. </h1> </div> ) } export default Hero
补充说明
如果你用的是React18的严格模式,开发环境下useEffect会执行两次,会导致实例重复创建,要么在开发环境临时关闭严格模式,要么在销毁方法里把所有事件监听、Three资源都清理干净即可。
内容的提问来源于stack exchange,提问作者DELORME Joris
相关产品推荐
相关产品推荐

