Search code examples
javascriptreactjstypescriptreact-hooks

Using multiple refs on a single React element


I'm using the useHover() react hook defined in this recipe. The hook returns a ref and a boolean indicating whether the user is currently hovering over element identified by this ref. It can be used like this...

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

  return (
    <div ref={hoverRef}>
      {isHovered ? 'Hovering' : 'Not Hovering'}
    </div>
  );
}

Now let's say that I want to use another (hypothetical) hook called useDrag which returns a ref and a boolean indicating whether the user is dragging the current element around the page. I want to use this on the same element as before like this...

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

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

This won't work because the ref prop can only accept a single reference object, not a list like in the example above.

How can I approach this problem so I can use multiple hooks like this on the same element? I found a package that looks like it might be what I'm looking for, but I'm not sure if I'm missing something.


Solution

  • A React ref is really nothing but a container for some mutable data, stored as the current property. See the React docs for more details.

    {
      current: ... // my ref content
    }
    

    Considering this, you should be able to sort this out by hand:

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