I’m trying to move my camera to a different position based on the route. I initially tried to use useFrame in a useEffect hook with the dependency set to location(from useLocation).pathname, but that didn’t work since you can’t call hooks within hooks. Then what I tried was making a function calling useFrame and setting that as the onClick action for the NavLink that changes the route:
const changeLocation = () => {
useFrame((state) => {
state.camera.position.lerp([1700, 500, 1250], 0.05);
state.camera.lookAt(0, 0, 0);
state.camera.updateProjectionMatrix();
});
};
This gave me the error invalid hook call, so I’m assuming I’m breaking a rule of useFrame somehow here. I also tried just calling the useFrame directly as my onClick function, but for some reason that gave me the error that useFrame has to be called directly within the Canvas, which I’m not sure why that happened since the function with the onClick listener was nested within my Canvas element.
Since none of these worked, I’m wondering if there’s actually a way to animate camera movement conditionally using useFrame? All of the errors lead me to believe that useFrame has to be called at the top of the function and can’t have any conditionals attached, but I could just be going about this wrong. Or should I not be using useFrame at all? I’m assuming I could just set camera position but ideally I want the movement to be animated rather than instant.
There aren’t rules to useFrame, it’s just a requestAnimationFrame
wrapped in a hook. Calling a hook inside it would lead to breaking the hook execution order, which is the issue, as useFrame
is executed ~60 frames per second in an async fashion.
Ain’t that how custom hooks are made? 
Quite a few ways to do that, you could use useThree
outside of the useFrame
to grab the camera and then work with it for example:
const { camera } = useThree();
useFrame(() => {
if (!camera) {
return;
}
camera.position.y += 1.0;
});
So then would I just be putting the conditional inside of the useFrame call itself rather than outside? For ex:
const location = useLocation();
const { camera } = useThree();
useFrame(() => {
if (!camera) {
return;
}
if (location.pathname = '/myroute') {
camera.position.lerp(someCoordinates, 0.05);
camera.lookAt(0, 0, 0);
} else if (location.pathname = '/myotherroute') {
camera.position.lerp(someOtherCoordinates, 0.05);
camera.lookAt(0, 0, 0);
}
});
And would the useFrame get called on every route change?
Since the useFrame hook is continuous, wouldn’t there be no way to call it again after the initial call? So for example I could conditionally move the camera to a certain position on initial render based on the initial path (assumedly just the root), but I wouldn’t be able to call it again once the path changes since that initial call never stops running. (Unless there’s a way to forcefully stop it from running, but I’m not sure if that would cause weird behavior)
It stops running as soon as the component it’s contained within unmounts - so if the component is not present in the new route, useFrame
will stop running as soon as you change the route.
Thank you, I was able to get it up and running!