.preload() -- how to pass prop to this when it's outside the renderer/component?

Hello,

I am loading models via a config file. The problem is that when I click the arrow to go next, it re-renders the app and plays a loading gif that was intended to be just for the inital render of the app.

I’m thinking I need to preload these glb files. I saw the .preload() and think this would work, however, it is outside the scope of the component .: I cannot pass the prop(path) down to it.

Is the only option redux toolkit/ context?

import React, { useRef } from ‘react’;
import { useLoader, useThree, useFrame } from ‘@react-three/fiber’;
import { GLTFLoader } from ‘three/examples/jsm/loaders/GLTFLoader’;
import { useDrag } from “@use-gesture/react”;
import { animated, useSpring } from “@react-spring/three”;
import * as THREE from “three”;

const Model = ({ path, scale = 1 }) => {
const { size, viewport } = useThree();
const aspect = size.width / viewport.width;
let planeIntersectPoint = new THREE.Vector3();
const floorPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);

const ref = useRef();
const { scene } = useLoader(GLTFLoader, path);

const [spring, api] = useSpring(() => ({
    from: { x: 0 },
    to: { x: 2 },
    scale: 1,
    config: { mass: 10, tension: 1000, friction: 50, precision: 0.0001 }
}));

const bind = useDrag(
    ({ active, movement: [x, y], timeStamp, event }) => {
        if (active) {
            event.ray.intersectPlane(floorPlane, planeIntersectPoint);
        }
        //   setIsDragging(active);
        //activate this when you want to disable the camera drag and import setIsDragging as a prop in the main function

        api.start({
            scale: active ? 1.2 : 1,
            rotation: [y / aspect * 0, x / aspect, 2],
            bounds: { left: 1 / 2, right: 1 / 2, top: 0 / 2, bottom: 0 / 2 },
        });
        return timeStamp;
    },
    { delay: false }
)

useFrame((state, delta) => {
    ref.current.rotation.y += 0.01;
});


return (
    <animated.mesh ref={ref} {...spring} {...bind()} castShadow receiveShadow  rotation = [0, 0, 0] position={[0, 0, 0]}>
        <primitive
            object={scene}
            scale={scale}
        />
    </animated.mesh>
)

}

export default Model;

thanks!

the arrow to go next? it sounds to me you just have a side effect. a side effect is when your component is beholden to how many times it renders, and it just shouldn’t matter how often. do you have a sandbox?

preload just loads and parses the model, useGLTF will do that too and then draw from cache. repeating useGLTF with the same url will never cause it to re-load or re-parse. i also don’t think reaching out, for redux of all things, is the solution, your problem is most likely a tiny bug somewhere, a missing ,[]) or something like that.

That makes sense! thank you for explaining that to me.

I wish I could share this repository but it’s private ( work ).
I have two arrows which go through a config file( an array) to load a new model.

I did notice on initial render that the useDrag/useSpring doesn’t work. Then when I press the arrow for the next model, the loading gif that plays when you visit the page plays again ( so the the page re-renders) but then I can use the animation.

Super strange behaviour

Here are is my carousel component:

const Carousel = (({ position, rotation, lookAt }) => {
const [currentIndex, setCurrentIndex] = useState(0);

const currentModel = useMemo(() => carouselConfig?.[currentIndex], [currentIndex]);

const handleArrowClick = useCallback((direction) => {
    if (direction === "left") {
        setCurrentIndex(currentIndex - 1 > 0 ? currentIndex - 1 : 0);
    } else {
        setCurrentIndex(currentIndex + 1 < carouselConfig.length ? currentIndex + 1 : currentIndex);
    }
}, [currentIndex]);

return (
    <group position={position} rotation={rotation}>
        <pointLight color="white" intensity={2} position={[80, -40, 0]} />
        <Exit lookAt={lookAt} />
        {
            currentModel && <Model path={currentModel.path} scale={currentModel.scale} rotation={currentModel.rotation} />
        }
        <Arrow direction="left" onClick={() => handleArrowClick("left")} />
        <Arrow direction="right" onClick={() => handleArrowClick("right")} />
        <OrbitControls />
    </group>
)

});
export default Carousel;

I wonder if it’s my logic in here?
or alternatively should we be loading these models but scaling them to 0 somewhere else on the page.