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

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

  1. 删除文件末尾的new WebGL()这行代码,改为导出WebGL类
  2. 调整类的构造函数,支持从外部传入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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 06:36:02