In React I am using @react-three/fiber
for the 3D stuff.
There are a lot components that don't support declarative way of doing things and require refs and imperative code (e.g. CameraControls
from drei).
I am trying to initialize a CameraControls
on the startup:
const cameraRef = useRef<CameraControls>(null)
const resetCamera = () => {
if (cameraRef.current == null) return
cameraRef.current.setLookAt(
...cameraInitialPosition,
...cameraInitialTarget,
true
)
}
useEffect(resetCamera, [])
return (
<Canvas shadows>
<CameraControls ref={cameraRef} />
...
)
Of course, this does not work since on the first and only time useEffect
is executed, the cameraRef.current
is still null
.
When I try with timeout hack, current is still null:
useEffect(() => {
setTimeout(resetCamera, 0)
}, [])
When I increase timeout to couple of hundred ms, it starts working:
useEffect(() => {
setTimeout(resetCamera, 500)
}, [])
This approach with timeout is hacky and bad, looking for something better.
Is there a way to write some custom hook that would return refObject
and later, when the current gets populated for the first time to execute the provided function?
You can store the ref with useState
, using the set state function as a callback function ref, instead of an object ref.
Since the set state would be called when the ref is available, the state would change, and the useEffect
would be triggered:
const [cameraRef, setCameraRef] = useState<CameraControls>(null)
const resetCamera = () => {
if (!cameraRef) return
cameraRef.setLookAt(...cameraInitialPosition, ...cameraInitialTarget, true)
}
useEffect(resetCamera, [cameraRef])
return (<Canvas shadows>
<CameraControls ref={setCameraRef} />
...
)
If you only use the ref to invoke setLookAt
once, and you don't need to store the ref, just call resetCamera
directly from the ref
prop of CameraControls
:
const resetCamera = (cameraRef: CameraControls) => {
if (!cameraRef) return
cameraRef.setLookAt(...cameraInitialPosition, ...cameraInitialTarget, true)
}
return (<Canvas shadows>
<CameraControls ref={resetCamera} />
...
)