pmndrs / react-three-fiber

🇨🇭 A React renderer for Three.js
https://docs.pmnd.rs/react-three-fiber
MIT License
27.25k stars 1.56k forks source link

useRef returns T or undefined, but NodeProps.ref only returns T #3124

Open arswaw opened 9 months ago

arswaw commented 9 months ago

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: ``` ``` If I define useRef in the same component like: `const cubeRef = useRef()` Then `ref` in the `` will return the following error: ``` Type 'MutableRefObject' is not assignable to type 'Ref, Material | Material[], Object3DEventMap>> | undefined'. Type 'MutableRefObject' is not assignable to type 'RefObject, Material | Material[], Object3DEventMap>>'. Types of property 'current' are incompatible. Type 'undefined' is not assignable to type 'Mesh, 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, Material | Material[], Object3DEventMap>> | undefined ``` The problem is that React useRef is typed like: `function useRef(): MutableRefObject;` Whereas in `r3f/node_modules/@react-three/fiber/dist/declarations/src/three-types.d.ts` we see: ``` export interface NodeProps { attach?: AttachType; /** Constructor arguments */ args?: Args

; children?: React.ReactNode; ref?: React.Ref; key?: React.Key; onUpdate?: (self: T) => void; } ``` If ref (and perhaps related attributes) were typed: `ref?: React.Ref` 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` 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?

ksoldau commented 6 months ago

I am experiencing the same issue with "@react-three/fiber": "^8.15.19".

CodyJasonBennett commented 6 months ago

I think the type we use in R3F is wrong and should allow undefined. We can align with the DOM types on this.

filahf commented 5 months ago

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} />;
};
CodyJasonBennett commented 5 months ago

This will be fixed with React 19. Looks like this was an upstream issue.