typescript 在React.forwardRef中使用ref.current

6za6bjd0  于 2023-04-13  发布在  TypeScript
关注(0)|答案(4)|浏览(211)

Codesandbox here
我尝试使用父组件的ref来侦听子组件中的某些ref事件,其中ref使用React.forwardRef附加到子组件。然而,当我引用ref.current时,我在子组件中收到了一个linting投诉,声明:
属性“current”在类型“Ref”上不存在。属性“current”在类型“(instance:HTMLDivElement)=〉void'
如何在React.forwardRef组件中引用ref?谢谢。

index.tsx:

import * as React from "react";
import ReactDOM from "react-dom";

const Component = React.forwardRef<HTMLDivElement>((props, ref) => {
  React.useEffect(() => {
    const node = ref.current;
    const listen = (): void => console.log("foo");

    if (node) {
      node.addEventListener("mouseover", listen);
    }
    return () => {
      node.removeEventListener("mouseover", listen);
    };
  }, [ref]);

  return <div ref={ref}>Hello World</div>;
});

export default Component;

const App: React.FC = () => {
  const sampleRef = React.useRef<HTMLDivElement>(null);

  return <Component ref={sampleRef} />;
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
hjqgdpho

hjqgdpho1#

Refs不一定是具有current属性的对象。它们也可以是函数。因此类型错误指出,您可能会传入后者之一。您需要编写代码,以便它可以与两种变体一起工作。
这可能有点棘手,但它是可行的。我们的效果不能附带在传入的函数上,因为该函数可以执行任何操作,并且没有考虑我们的useEffect。因此我们需要创建自己的ref,我称之为myRef。
在这一点上,现在有两个参考:传入的一个,以及我们创建的本地的一个。为了填充它们,我们需要自己使用refs的函数形式,并且在该函数中,我们可以将div元素分配给两个refs:

const Component = React.forwardRef<HTMLDivElement>((props, ref) => {
  const myRef = useRef<HTMLDivElement | null>(null);
  React.useEffect(() => {
    const node = myRef.current;
    const listen = (): void => console.log("foo");

    if (node) {
      node.addEventListener("mouseover", listen);
      return () => {
        node.removeEventListener("mouseover", listen);
      };
    }
  }, [ref]);

  return (
    <div ref={(node) => {
      myRef.current = node;
      if (typeof ref === 'function') {
        ref(node);
      } else if (ref) {
        ref.current = node;
      }
    }}>Hello World</div>
  );
});
klsxnrf1

klsxnrf12#

我四处搜索,因为这是另一个钩子(如useForwardRef)的一个很好的候选对象。
我自己也试过,效果很好。

const InputField = React.forwardRef<HTMLInputElement, InputFieldProps>(
  (props, ref) => {
   const inputRef = useForwardRef<HTMLInputElement>(ref);
   const onLabelClick = () => {
    inputRef.current?.focus();
  };

  return <input ref={inputRef} />
 );

当然,这本质上与初始答案中的代码相同,只是写成了一个钩子。

const useForwardRef = <T,>(
  ref: ForwardedRef<T>,
  initialValue: any = null
) => {
  const targetRef = useRef<T>(initialValue);

  useEffect(() => {
    if (!ref) return;

    if (typeof ref === 'function') {
      ref(targetRef.current);
    } else {
      ref.current = targetRef.current;
    }
  }, [ref]);

  return targetRef;
};

**注意:**钩子的名字是有争议的,可以叫useCopyRefuseBorrowRef什么的。这里为了简单起见,因为它是为了forwardRef而创建的,所以我们命名为useForwardRef,但实际上它与转发无关。

ukxgm1gy

ukxgm1gy3#

详细阐述@Nicholas回答:

import React, { MutableRefObject, Ref, useEffect } from "react";
import { TextInput } from "react-native";

type CustomTextInputProps = {};

export const CustomTextInput = React.forwardRef<
  TextInput,
  CustomTextInputProps
>((props, ref) => {
  const localRef = React.useRef<TextInput | null>(null);

  useEffect(() => {
    // using the local ref
    localRef.current?.focus();
  }, []);

  return <TextInput {...props} ref={assignRefs(localRef, ref)} />;
});

const assignRefs = <T extends unknown>(...refs: Ref<T | null>[]) => {
  return (node: T | null) => {
    refs.forEach((r) => {
      if (typeof r === "function") {
        r(node);
      } else if (r) {
        (r as MutableRefObject<T | null>).current = node;
      }
    });
  };
};
cdmah0mi

cdmah0mi4#

我们也可以这样做。首先创建一个这样的实用程序:

function useCombinedRefs(...refs) {
  const targetRef = React.useRef()

  React.useEffect(() => {
    refs.forEach(ref => {
      if (!ref) return
      if (typeof ref === 'function') ref(targetRef.current)
      else ref.current = targetRef.current
    })
  }, [refs])

  return targetRef
}

并像这样使用它:

const CustomInput = React.forwardRef((props, ref) => {
    const innerRef = React.useRef(null)
    const combinedRef = useCombinedRefs(ref, innerRef)

    return (
      <input ref={combinedRef} />
    )
})

来源及更多信息:Reusing the ref from forwardRef with React hooks

相关问题