react-three-fiber
react-three-fiber copied to clipboard
useRef returns T or undefined, but NodeProps.ref only returns T
Discussed in https://github.com/pmndrs/react-three-fiber/discussions/3121
Originally posted by arswaw December 14, 2023 Hello. I am using R3F for the first time and I am also using TypeScript. All my component files have the .tsx extension.
I have run into an issue with useRef and this may affect other attributes.
Consider the following code:
<mesh ref={cubeRef} rotation-y={Math.PI * 0.25} position-x={2} scale={1.5}>
<boxGeometry />
<meshBasicMaterial color="mediumpurple" />
</mesh>
If I define useRef in the same component like:
const cubeRef = useRef()
Then ref in the <mesh ... /> will return the following error:
Type 'MutableRefObject<undefined>' is not assignable to type 'Ref<Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>> | undefined'.
Type 'MutableRefObject<undefined>' is not assignable to type 'RefObject<Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>>'.
Types of property 'current' are incompatible.
Type 'undefined' is not assignable to type 'Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap> | null'.ts(2322)
three-types.d.ts(34, 5): The expected type comes from property 'ref' which is declared here on type 'MeshProps'
(property) ref?: React.Ref<Mesh<BufferGeometry<NormalBufferAttributes>, Material | Material[], Object3DEventMap>> | undefined
The problem is that React useRef is typed like:
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
Whereas in r3f/node_modules/@react-three/fiber/dist/declarations/src/three-types.d.ts we see:
export interface NodeProps<T, P> {
attach?: AttachType;
/** Constructor arguments */
args?: Args<P>;
children?: React.ReactNode;
ref?: React.Ref<T>;
key?: React.Key;
onUpdate?: (self: T) => void;
}
If ref (and perhaps related attributes) were typed:
ref?: React.Ref<T | undefined>
then it would solve the problem.
When I modified the R3F dependency directly, the error disappeared.
Since that is bad practice, I have used the temporary solution:
const cubeRef = useRef() as MutableRefObject<Mesh>
However this decreases type safety since the ref could return undefined at runtime.
I am not sure if I am in misinterpreting the problem or the solution. Does my new union type make sense?
I am experiencing the same issue with "@react-three/fiber": "^8.15.19".
I think the type we use in R3F is wrong and should allow undefined. We can align with the DOM types on this.
I think this might be the correct behavior, you'll get the same error with a regular DOM element. You need to type your ref like this and init with null. (React will set ref.current = null when the component unmounts)
export const Component = () => {
const ref = useRef<Mesh>(null);
return <mesh ref={ref} />;
};
Respectively, this is how you would type a ref to a div
export const Component = () => {
const ref = useRef<HTMLDivElement>(null);
return <div ref={ref} />;
};
This will be fixed with React 19. Looks like this was an upstream issue.