I am trying to build an 3d animation w/ React and Three.js, where essentially a car starts moving forwards, therefore i decrease the value of it’s z position in the axis using the useRef hook. When it reaches a certain point (f.e. -100) i set the state of the animation to false, the animation stops, but when i try to reset its z position to the initial value,it won’t happen. What i noticed inside my if statement, is that even though i change the state of startCarAnimation value, the value is still true in the console. I tried changing the value by using a useEffect hook with the startCarAnimation as a dependency, but to no aveil.
Here are my Components. The Timer component just sets the startCarAnimation to true after certain events
App.jsx
import React , { Suspense, useState } from 'react';
import { Canvas } from '@react-three/fiber';
import Scene from './Scene';
import Timer from './Timer';
function App() {
const [startCarAnimation, setStartCarAnimation] = useState(false)
return (
<main >
<Timer setStartCarAnimation={setStartCarAnimation}/>
<Suspense fallback={null}>
<Canvas shadows>
<Scene startCarAnimation={startCarAnimation} setStartCarAnimation={setStartCarAnimation} />
</Canvas>
</Suspense>
</main>
);
}
export default App;
Scene.jsx
import React, { useRef, useState, useEffect } from "react";
import { PerspectiveCamera } from "@react-three/drei";
import { useFrame,useLoader} from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
export default function Scene({ startCarAnimation, setStartCarAnimation }) {
const gltf = useLoader(GLTFLoader, '../public/assets/car/scene.gltf')
const groupRef = useRef();
const cameraRef = useRef();
const targetPosition = -100;
const [zPosition, setZPosition] = useState(0);
useFrame((state, delta) => {
if (startCarAnimation) {
groupRef.current.position.z = zPosition;
setZPosition((prevScale) => Math.max(prevScale - 2, targetPosition));
gltf.materials.tyres.map.rotation += 0.1;
if(targetPosition == zPosition){
setStartCarAnimation(false)
setZPosition(0)
console.log(startCarAnimation)
}
}
});
return (
<>
<PerspectiveCamera ref={cameraRef} makeDefault fov={30} position={[0, 0, 0.52]} />
<group ref={groupRef} position={[0, -0.9 , 0]} rotation={[0,Math.PI,0]} >
<directionalLight intensity={8} position={[0,0,1]}/>
<ambientLight intensity={3}/>
<primitive object={gltf.scene} />
</group>
</>
);
}
2
Answers
After reading the docs, which they state ''Be careful about what you do inside useFrame! You should never setState in there! '' .
i ditched the useState hook for the zPosition and just changed the position of the ref, until i reached the desired value.Then i reset the position of z and the startCarAnimation to false, to stop the animation.
Please always read the docs before posting here. They explicitly tell you that
setState
calls will not work correctly withinuseFrame()
hooks. You would need to stop your animation in another way