Search code examples
reactjstypescriptuse-refreact-forwardref

How to correctly access in a type-safe way the ref.current of a forwardRef element in React?


Here's a minimal example o the type-safety problem:

https://codesandbox.io/s/mystifying-dream-8v7kgu?file=/src/App.tsx

const CanvasContainer = forwardRef<HTMLCanvasElement>((props, parentRef) => {
  const localRef = useRef<HTMLCanvasElement>(null);
  const canvasRef = parentRef ?? localRef;

  useEffect(() => {
    // type-safety no longer works here, see code sandbox link
    console.log(canvasRef.current);
  }, []);

  return <canvas ref={canvasRef} />;
});

With typescript, the canvasRef can be more than just a React.RefObject<HTMLCanvasElement>. Why, and how can we achieve type-safety and access the .curent value in this situation?

Note:

  • The localRef and parentRef pattern was taken from Optional forwardRef that is required inside - ReactJS and appears to be a decent way of normalising optional refs sent this way.
  • This is an isolated case where I want to figure out how to support this kind of code pattern. There are other ways to achieve the functionality that I am implementing without forwardRef. I'm not asking for a solution related to my canvas feature, but for a clarification of how react and the types of react can be tamed.

Solution

  • I ended up using this solution, since I am sure I only want to support normal refs or no refs at all and like the first comment of the question states (from Nikos), the ref could also be a setter function.

    const CanvasContainer = forwardRef<HTMLCanvasElement>((props, parentRef) => {
        if (typeof parentRef === "function") {
            throw new Error(
                `Only React Refs that are created with createRef or useRef are supported`
            )
        }
    
        const localRef = useRef<HTMLCanvasElement>(null)
        const canvasRef = parentRef ?? localRef
    // ...