我可以将多个引用分配给同一个元素/节点吗?

Gok*_*ari 3 javascript typescript reactjs react-ref

我有一个函数组件,它将传入的引用从父组件转发到div它正在渲染的组件。我还想在组件内创建一个 ref 并将其分配给同一个div. 但我不能,因为一个元素只需要一个引用。我是否做错了什么或者有解决类似问题的方法吗?

来自ref父级的是 a React.Ref,但我需要React.RefObject将其传递给像react-use这样的第3方挂钩clickAway: https: //github.com/streamich/react-use/blob/master/docs/useClickAway.md

这是示例组件:

import React, { useRef } from 'react';

type Props = React.PropsWithoutRef<JSX.IntrinsicElements['div']>;

function Component({ ...props }: Props, ref: React.Ref<HTMLDivElement>) {
  const wrapper = useRef<HTMLDivElement>(null);

  return (
    <div
      {...props}
      ref={ref}
      // ref={wrapper}
    />
  );
}

export default React.forwardRef(Component);
Run Code Online (Sandbox Code Playgroud)

won*_*ngz 7

简答


您可以使用像这样的简单函数来合并引用
const mergeRefs = (...refs) => {
  return node => {
    for (const ref of refs) {
      ref.current = node
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

为什么它有效


这样做的原因是,在幕后, ref 属性可以接受带有自引用节点作为参数的函数。
import React from "react"

export default () => {
  const ref1 = React.useRef(null)
  const ref2 = React.useRef(null)
  return (
    <div
      ref={node => {
        ref1.current = node
        ref2.current = node
      }}
    >
      Hello
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

这些示例归功于本文

useRef 键入


从类型上看,它看起来像这样。就我个人而言,我不清楚如何阅读“RefCallback” - 它看起来像一个带有通用参数 T 的自引用函数,并且以某种方式 T 代表节点本身。等待有人进一步澄清。
interface RefObject<T> {
    readonly current: T | null;
}
type RefCallback<T> = { bivarianceHack(instance: T | null): void }["bivarianceHack"];
type Ref<T> = RefCallback<T> | RefObject<T> | null;
Run Code Online (Sandbox Code Playgroud)

打字稿答案


如果你想添加更多的检查和平衡,你可以使用来自 Tailwind 的 headlessui 的这个 typescript 兼容函数。
function useSyncRefs<TType>(
  ...refs: (
    | React.MutableRefObject<TType | null>
    | ((instance: TType) => void)
    | null
  )[]
) {
  let cache = React.useRef(refs);

  React.useEffect(() => {
    cache.current = refs;
  }, [refs]);

  return React.useCallback(
    (value: TType) => {
      for (let ref of cache.current) {
        if (ref == null) {
          console.log('ref is null');
          continue;
        }
        if (typeof ref === 'function') {
          console.log('ref is a function. Returning called function');
          ref(value)
        } else {
          console.log('returning the value: ', value);
          ref.current = value
        };
      }
    },
    [cache]
  );
};
Run Code Online (Sandbox Code Playgroud)

您可以在这个世博小吃中看到它的实际效果。