1
votes

I have two components: App and Child. I use useRef hook to get the DOM element in Child component and do some work with it.

I also pass ref from App component to Child component using React.forwardRef API.

App.tsx:

import { useRef } from "react";
import Child from "./Child";

export default function App() {
  const ref = useRef(null);
  console.log("App ref: ", ref);
  return (
    <div className="App">
      <Child ref={ref} />
    </div>
  );
}

Child.ts:

import React, { useEffect, useRef } from "react";

export default React.forwardRef((props, ref) => {
  const innerRef = useRef<HTMLDivElement>(null);
  const divElement = ref || innerRef; // TSC throw error here.
  useEffect(() => {
    if (divElement.current) {
      console.log("clientWidth: ", divElement.current.clientWidth);
      // do something with divElement.current
    }
  }, []);
  return <div ref={divElement}></div>;
});

I am not sure it's a correct way to use innerRef and forwarded ref together like above. At least, the TSC throw an error which means it does NOT allow use it like this.

Type '((instance: unknown) => void) | MutableRefObject' is not assignable to type 'string | ((instance: HTMLDivElement | null) => void) | RefObject | null | undefined'. Type 'MutableRefObject' is not assignable to type 'string | ((instance: HTMLDivElement | null) => void) | RefObject | null | undefined'. Type 'MutableRefObject' is not assignable to type 'RefObject'. Types of property 'current' are incompatible. Type 'unknown' is not assignable to type 'HTMLDivElement | null'. Type 'unknown' is not assignable to type 'HTMLDivElement'.ts(2322) index.d.ts(143, 9): The expected type comes from property 'ref' which is declared here on type 'DetailedHTMLProps<HTMLAttributes, HTMLDivElement>'

I want to get the correct ref in App component and use this ref in Child component as well. How can I do this?

codesandbox

1
I think this is the answer you are looking for stackoverflow.com/a/65877297/6106583 - Matin Kajabadi

1 Answers

2
votes

You can largely ignore the TS error because you didn't provide types for the forwardRef arguments so it doesn't know that ref/innerRef are equivalently typed.

const divElement = ref || innerRef; <div ref={divElement}></div>

I don't understand this bit - do you want it to have a reference to the div element directly regardless of if the is given a ref or not?

Anyway, I think if you want to get it working like this, it's best you use the useImperativeHandle hook like useImperativeHandle(ref, () => innerRef.current);

So, the Child component itself manages the ref with the innerRef but if someone does pass a ref to the Child component, it will yield that.


export default React.forwardRef((props, ref) => {
  const innerRef = useRef<HTMLDivElement>(null);

  useImperativeHandle(ref, () => innerRef.current);

  useEffect(() => {
    if (innerRef.current) {
      console.log("clientWidth: ", innerRef.current.clientWidth);
    }
  }, []);

  return <div ref={innerRef}></div>;
});