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

React Three Fiber/Drei组件useEffect中useRef为null的问题求助

解决React Three Fiber初始加载时useRef为null的动画触发问题

问题原因

Canvas内部的3D组件是Three.js对象,并非普通DOM元素,其初始化时机晚于React组件的初始渲染周期。普通useEffect依赖cylinderRef.current无法捕获到ref的赋值时机——因为ref的current属性更新不会触发React的依赖重新计算。

解决方案

方法一:使用onCreated回调(推荐)

利用@react-three/fiber组件的onCreated事件,在3D对象创建完成后直接触发动画:

import React, { useRef, Suspense } from 'react';
import { Canvas } from '@react-three/fiber';
import { Cylinder, OrbitControls } from '@react-three/drei';
import gsap from 'gsap';

function Section() {
  const cylinderRef = useRef(null);

  const handleMeshCreated = (mesh) => {
    cylinderRef.current = mesh;
    // 执行动画
    const timeLine = gsap.timeline();
    timeLine.to(mesh.position, {
      y: .7,
      duration: 3,
      ease: "power2.out",
    });
  };
  
  return(
    <Canvas>
      <Suspense fallback={<></>}>
        <mesh position={[0,2,0]} onCreated={handleMeshCreated}>
          <Cylinder scale={[0.9, 0.5, 0.9]}>
            <meshStandardMaterial color={"#545755"} roughness={0.7}/>
          </Cylinder>
        </mesh>
        <OrbitControls enableZoom={false} autoRotate={false}/>
        <ambientLight intensity={1}/>
        <directionalLight position={[3,2,1]}/>
      </Suspense>
    </Canvas>
  )
}

方法二:使用useFrame钩子

通过渲染循环检查ref状态,触发动画后停止监听:

import React, { useRef, useFrame, Suspense } from 'react';
import { Canvas } from '@react-three/fiber';
import { Cylinder, OrbitControls } from '@react-three/drei';
import gsap from 'gsap';

function Section() {
  const cylinderRef = useRef(null);
  const animationTriggered = useRef(false);

  useFrame(() => {
    if (cylinderRef.current && !animationTriggered.current) {
      animationTriggered.current = true;
      const timeLine = gsap.timeline();
      timeLine.to(cylinderRef.current.position, {
        y: .7,
        duration: 3,
        ease: "power2.out",
      });
    }
  });
  
  return(
    <Canvas>
      <Suspense fallback={<></>}>
        <mesh position={[0,2,0]} ref={cylinderRef}>
          <Cylinder scale={[0.9, 0.5, 0.9]}>
            <meshStandardMaterial color={"#545755"} roughness={0.7}/>
          </Cylinder>
        </mesh>
        <OrbitControls enableZoom={false} autoRotate={false}/>
        <ambientLight intensity={1}/>
        <directionalLight position={[3,2,1]}/>
      </Suspense>
    </Canvas>
  )
}

方法三:结合状态变量监听ref变化

通过状态变量显式触发useEffect,感知ref的赋值:

import React, { useRef, useEffect, useState, Suspense } from 'react';
import { Canvas } from '@react-three/fiber';
import { Cylinder, OrbitControls } from '@react-three/drei';
import gsap from 'gsap';

function Section() {
  const cylinderRef = useRef(null);
  const [meshReady, setMeshReady] = useState(false);

  useEffect(() => {
    if (meshReady) {
      const timeLine = gsap.timeline();
      timeLine.to(cylinderRef.current.position, {
        y: .7,
        duration: 3,
        ease: "power2.out",
      });
    }
  }, [meshReady]);
  
  return(
    <Canvas>
      <Suspense fallback={<></>}>
        <mesh position={[0,2,0]} ref={(el) => {
          cylinderRef.current = el;
          if (el) setMeshReady(true);
        }}>
          <Cylinder scale={[0.9, 0.5, 0.9]}>
            <meshStandardMaterial color={"#545755"} roughness={0.7}/>
          </Cylinder>
        </mesh>
        <OrbitControls enableZoom={false} autoRotate={false}/>
        <ambientLight intensity={1}/>
        <directionalLight position={[3,2,1]}/>
      </Suspense>
    </Canvas>
  )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 23:25:19