typescript 如何正确地使用useRef和forwardRef?

hc8w905p  于 2023-06-24  发布在  TypeScript
关注(0)|答案(2)|浏览(279)

我有两个组件:AppChild。我使用useRef钩子来获取Child组件中的DOM元素,并对它做一些工作。
我还使用React.forwardRef API将refApp组件传递到Child组件。
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>;
});

我不确定像上面那样一起使用innerRef和转发ref是否正确。至少,TSC抛出一个错误,这意味着它不允许这样使用它。
类型'((示例:unknown)=> void)|MutableRefObject '不可分配给类型' string|((示例:HTMLDivElement| null)=> void)|RefObject|零|未定义类型“MutableRefObject”不可分配给类型“string|((示例:HTMLDivElement| null)=> void)|RefObject|零|未定义类型“MutableRefObject”不能分配给类型“RefObject”。属性“current”的类型不兼容。类型“unknown”不能分配给类型“HTMLDivElement| null'.类型'unknown'不可分配给类型'HTMLDivElement'. ts(2322)index. d. ts(143,9):预期的类型来自属性“ref”,该属性在类型“DetailedHTMLProps <HTMLAttributes,HTMLDivElement>”上声明
我想在App组件中获得正确的ref,并在Child组件中使用此ref。我该怎么做?
codesandbox

igetnqfo

igetnqfo1#

您可以在很大程度上忽略TS错误,因为您没有为forwardRef参数提供类型,因此它不知道ref/innerRef是等效类型的。
const divElement = ref || innerRef;<div ref={divElement}></div>
我不明白这一点--你想让它直接引用div元素,而不管是否给了一个ref?
无论如何,我认为如果你想让它像这样工作,最好使用useImperativeHandle钩子,比如useImperativeHandle(ref, () => innerRef.current);
因此,Child组件本身使用innerRef管理ref,但如果有人向Child组件传递ref,它将产生该ref。

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>;
});
esbemjvw

esbemjvw2#

如果你正在寻找一个使用TypeScript而不使用useImperativeHandle的解决方案,你可以这样写:

interface Props {
  children: React.ReactNode;
  // more...
}

export default React.forwardRef<HTMLDivElement, Props>(({ children }, ref) => {
  const innerRef = useRef<HTMLDivElement>();
   
  const setRef = (element: HTMLDivElement) => {
    if (typeof ref === "function") ref(element);
    else if (ref) ref.current = element;
    innerRef.current = element;
    console.log("clientWidth: ", innerRef.current.clientWidth);
  };
    
  return <div ref={setRef}>{children}</div>;
});

示例:https://codesandbox.io/s/beautiful-shadow-n83nmw?file=/src/App.tsx
它工作的原因是ref prop 接受回调,因此当它触发时,您可以执行任何您喜欢的代码。这里,我们设置了ref值(我们说它可以是一个函数,因此if/else)和innerRef
注意:useImperativeHandle没有任何问题,请随意使用。两种解决方案的性能相同。

相关问题