reactjsreact-three-fiberreact-three-drei

useRef of React Three fiber/drei Component undefined/null in useEffect


I am trying to trigger a transition/movement for a React Three drei component on initial page load by accessing its ref in a useEffect function. Within the useEffect on initial load the useRef is still null.

import React, { useRef useEffect, 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);

  useEffect(()=>{
    if(!!cylinderRef.current){ //At this check cylinderRef.current is still null
      // The movement is executed here with gsap.timeline
      // which needs to access the cylinderRef.current
      const timeLine = gsap.timeline({paused:true});
      timeLine.to(cylinderRef.current.position, {
        y: .7,
        duration: 3,
        ease: "power2.out",
      });
      timeLine.play();
    };
  }, [cylinderRef.current]); // Adding this parameter to useEffect did not help
  
  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>
  )
}

I have seen some talk about using a useCallback in a similar situation, but I am not exactly sure that is the right solution for this use case and for function based components.

The issue still happens with or without the Suspense component, which will eventually be used to display a loading icon.


Solution

  • Work-around for me was using the onStart callback to set the mouseButtons. For you, this would be the onChange callback.

    export default function App() {
      const handleStart = (ev) => {
        ev.target.mouseButtons.left = ACTION.TRUCK;
      };
      return (
        <div className="App">
          <Canvas>
            <orthographicCamera />
            <CameraControls onStart={handleStart} />
          </Canvas>
        </div>
      );
    }