reactjsthree.jsreact-three-fiber

Call async function in Three.js useFrame?


I need to make a fetch call when the camera’s position crosses a threshold. I need the function to be callable again, but not until after 1000ms has passed.

Ideally this would happen immediately, but a small delay is tolerable. My main concern is performance as I’m rendering a very complex scene.

import { useMemo, useEffect } from "react";
import { useThree, useFrame } from "@react-three/fiber";
import throttle from "lodash/throttle";

export default function Component() {
  const { camera } = useThree();

  const throttledFetch = useMemo(
    () =>
      throttle(
        (positionY: number) => {
          fetch(`/something?positionY=${positionY}`).then((res) => {
            console.log(res);
          });
        },
        1000,
        { leading: true, trailing: false }
      ),
    []
  );

  useFrame(() => {
    if (camera.position.y < 1.5) {
      throttledFetch(camera.position.y);
    }
  });

  useEffect(() => {
    return () => {
      throttledFetch.cancel();
    };
  }, [throttledFetch]);

  return null;
}

This code works, but is it a sensible approach?


Solution

  • Minimal improvement using a useRef instead of lodash.throttle Avoids an external dependency and keeps control tight.

    import { useRef } from "react";
    import { useThree, useFrame } from "@react-three/fiber";
    
    export default function Component() {
      const { camera } = useThree();
      const lastCall = useRef(0);
    
      useFrame(() => {
        if (camera.position.y < 1.5) {
          const now = Date.now();
          if (now - lastCall.current > 1000) {
            lastCall.current = now;
            fetch(`/something?positionY=${camera.position.y}`).then(res => {
              console.log(res);
            });
          }
        }
      });
    
      return null;
    }