Using react-three-fiber often requires using refs to manipulate objects in the scene. For example, we might want to move a mesh's position on every frame render (see code below).
However, refs in typescript are always potentially undefined. So we have to check if our meshRef
is current before we manipulate it:
useFrame((state) => {
if (meshRef.current) {
meshRef.current.position.add(new Vector3(1, 1, 1));
}
});
But I imagine that we want the code in our useFrame
hook to be highly optimized. If we were using regular javascript react, there'd be no need for the if(meshRef.current)
. So it seems that just by using typescript, we're sacrificing some performance.
Is there any way around this that doesn't involve telling the typescript compiler to ignore the potential undefined value? i.e //@ts-ignore
Full code example:
import React, { useRef } from "react";
import { Canvas, extend, useFrame, useThree } from "@react-three/fiber";
import "./styles.css";
import { Mesh, Vector3 } from "three";
const Scene = (): JSX.Element => {
const meshRef = useRef<Mesh>();
useFrame((state) => {
if (meshRef.current) {
meshRef.current.position.add(new Vector3(1, 1, 1));
}
});
return (
<Canvas camera={{ fov: 75, position: [0, 0, 70] }}>
<mesh ref={meshRef}>
<boxBufferGeometry args={[10, 10, 1]} />
</mesh>
</Canvas>
);
};
export default Scene;
You can use the !
suffix to force treating an optional property as a required one.
const ref = useRef<{ someObj: true }>()
ref.current!.someObj // typescript allows this
However, this isn't type safe. And will definitely crash the first time it's run when the ref is undefined
. But if you can be absolutely sure that this code only runs when the ref has a value, then it should work.
I wouldn't recommend it though. You're inviting a crash where you didn't realize that the value could actually be missing there.
The second reason I wouldn't recommend it is that this check you are trying to avoid is really really fast.
Checkout this benchmark where it compares just doing the thing:
for (const obj of data) {
result = result + obj.num;
}
and where you check for the presence of a thing and then do the thing:
for (const obj of data) {
if (obj.num) {
result = result + obj.num;
}
}
On my machine, the second one is slower by less than 5%. And that's the worst case scenario of checking 1,000 times per loop. If you check the presence of a value just once per frame, the performance impact will be so miniscule.
In short, nullish checks like these are not why your webgl application is slow.