requestAnimationFrame in React-Three-Fiber

I want to know can i fire animationFrame when needed. Actually now I has created basic Box component with react-three-fiber and not used useFrame hook into that, but in my browser performance view i see that animation frame fired. But in three js we can fire animationFrame when needed to do animation.

type axis = 'x' | 'y' | 'z'
type rotation = {
    state: true | false,
    axis: axis,
    speed: number
}

export type BoxProps = {
    position?: Vector3,
    rotation?: Euler,
    color?: Color,
    geometryArgs?: [width:number, height:number, depth:number],
    hoveredColor?: Color,
    activeScale?: number
    rotate?: rotation
}
import React, { useRef, useState } from "react"
import { BufferGeometry, Color, Material, Mesh, Vector3 } from "three"
import { BoxProps } from "./BoxProps"
import "@react-three/fiber"

export const Box = (props: BoxProps): React.ReactElement => {

    const mesh: React.Ref<Mesh<BufferGeometry, Material>>  = useRef(null)

    const [hovered, setHovered] = useState(false)
    const [active, setActive] = useState(false)

    const {
        geometryArgs = [1,1,1],
        position = new Vector3(0,0,0),
        color = new Color(0,0,0),
        hoveredColor = new Color(0.5,0.5,0.5),
        activeScale = 2,
    } = props

    return (
        <mesh 
            ref={mesh} 
            position={position}
            onClick={():void=>setActive(!active)}
            onPointerOver={():void=>setHovered(true)}
            onPointerOut={():void=>setHovered(false)}
            scale={active ? activeScale : 1}
        >
            <boxGeometry args={geometryArgs}/>
            <meshBasicMaterial color={hovered? hoveredColor: color}/>
        </mesh>
    )
}
function App() {
  return (
    <Canvas>
      <Box position={new Vector3(-2,0,0)} geometryArgs={[2,2,2]} />
      <Box position={new Vector3(2,0,0)} geometryArgs={[2,2,2]} />
    </Canvas>
  );
}


most webgl content is loop driven by default, fiber will also start a renderloop. that is perfectly fine and recommended. a running frameloop has often even performance benefits because it doesn’t need “warm up”, think of it as a running motor.

in any case you should not call requestAnimationFrame directly because more than 1 rafs impact performance badly. if you have something to animate you place it into useFrame, all useFrames will run in the same raf.

if you do not want a loop you can tell fiber that:

<Canvas frameloop="demand">

if you now look into devtools the timeline is empty (example). this will only render when a prop changes on any element. you can request a new frame/render by calling invalidate. this is what drei/orbitcontrols does for instance:

export function OrbitControls(props) {
    const invalidate = useThree((state) => state.invalidate)

    React.useEffect(() => {
      const callback = () => invalidate()
      controls.addEventListener('change', callback)
      return () => controls.removeEventListener('change', callback)
   }, [])

that means now it will render when the user orbits the camera.

1 Like

@drcmda Thank you for answer, also i find that solution after posting this topic in react-three-fiber docs optimization section. Now i’m busy with researching and learning of react-three-fiber, just for that i starts thing about optimization and performance problems and his solutions in react-three-fiber anyway. Really grate thanks to you for your answer