在单个 React 元素上使用多个引用

Kie*_*ran 20 javascript typescript reactjs react-hooks

我正在使用此配方useHover()中定义的反应钩子。该钩子返回 a和一个布尔值,指示用户当前是否将鼠标悬停在 this 标识的元素上。它可以像这样使用...refref

function App() {
  const [hoverRef, isHovered] = useHover();

  return (
    <div ref={hoverRef}>
      {isHovered ? 'Hovering' : 'Not Hovering'}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

现在假设我想使用另一个(假设的)钩子,useDrag它返回 aref和一个布尔值,指示用户是否在页面周围拖动当前元素。我想在与以前相同的元素上使用它,就像这样......

function App() {
  const [hoverRef, isHovered] = useHover();
  const [dragRef, isDragging] = useDrag();

  return (
    <div ref={[hoverRef, dragRef]}>
      {isHovered ? 'Hovering' : 'Not Hovering'}
      {isDragging ? 'Dragging' : 'Not Dragging'}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

这是行不通的,因为refprop 只能接受单个引用对象,而不是像上面示例中那样的列表。

我该如何解决这个问题,以便我可以在同一个元素上使用多个这样的钩子?我找到了一个看起来可能是我正在寻找的,但我不确定是否缺少某些东西。

小智 34

下面记录了一种简单的方法。

注意:ref元素上的属性采用一个函数,并且稍后在可用时使用元素或节点调用该函数。

function App() {
  const myRef = useRef(null);

  return (
    <div ref={myRef}>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

因此,myRef上面是一个具有定义的函数

function(element){
 // something done here
}
Run Code Online (Sandbox Code Playgroud)

所以一个简单的解决方案如下所示

function App() {
  const myRef = useRef(null);
  const anotherRef = useRef(null);

  return (
    <div ref={(el)=> {myRef(el); anotherRef(el);}}>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • `未捕获的类型错误:myRef 不是函数`。请改用“myRef.current = el”。 (15认同)
  • @holem `current` 是只读属性。 (6认同)
  • 谢谢。简单的解决方案正是我所需要的。TypeScript 变体 `ref={((el: HTMLDivElement) =&gt; setNodeRef(el)) &amp;&amp; ref}` (2认同)

Tim*_*imo 22

React ref 实际上只不过是一些可变数据的容器,存储为属性current。有关更多详细信息,请参阅React 文档。

{
  current: ... // my ref content
}
Run Code Online (Sandbox Code Playgroud)

考虑到这一点,您应该能够手动解决这个问题:

function App() {
  const myRef = useRef(null);

  const [hoverRef, isHovered] = useHover();
  const [dragRef, isDragging] = useDrag();

  useEffect(function() {
    hoverRef.current = myRef.current;
    dragRef.current = myRef.current;
  }, [myRef.current]);

  return (
    <div ref={myRef}>
      {isHovered ? 'Hovering' : 'Not Hovering'}
      {isDragging ? 'Dragging' : 'Not Dragging'}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 正确的想法,但是分配给 ref 不会导致渲染,因此 `useEffect` 将不会被正确调用。分配应该发生在回调中。 (12认同)

Rom*_*n86 12

我使用 TypeScript,它不允许轻松分配.current使用useRef或转发的引用创建的引用(因为它也可以是一个RefCallback)。因此,我创建了一个函数,可以巧妙地将所有给定的引用合并为一个RefCallback,以便干净/简单地使用。看一下这个。对我来说效果很好。

import { type MutableRefObject, type RefCallback } from 'react';

type MutableRefList<T> = Array<RefCallback<T> | MutableRefObject<T> | undefined | null>;

export function mergeRefs<T>(...refs: MutableRefList<T>): RefCallback<T> {
  return (val: T) => {
    setRef(val, ...refs);
  };
}

export function setRef<T>(val: T, ...refs: MutableRefList<T>): void {
  refs.forEach((ref) => {
    if (typeof ref === 'function') {
      ref(val);
    } else if (ref != null) {
      ref.current = val;
    }
  });
}
Run Code Online (Sandbox Code Playgroud)

使用示例

interface Props {
  // I pass it as a prop, you may use forwardRef - also supported by mergeRefs
  inputRef?: Ref<HTMLInputElement>;
}

export function Input({
  inputRef,
}: Props): ReactElement {
  // some local ref
  const privateRef = useRef<HTMLInputElement>(null);

  return (
    <input
      type="text"
      ref={mergeRefs(inputRef, privateRef)}
      // ... your other stuff
    />
  );
}
Run Code Online (Sandbox Code Playgroud)