1

I'm using React & TypeScript in a project and recently pieced together the following hook:

export const useDrag = (selectionRef: React.MutableRefObject<HTMLDivElement>) => {
  const [dragStart, setDragStart] = useState<Coordinates>(null);
  const [dragEnd, setDragEnd] = useState<Coordinates>(null);

  const onMouseDown = (e: MouseEvent) => setDragStart(getMouseOffset(e, selectionRef.current));
  const onMouseUp = () => {
    setDragStart(null);
    setDragEnd(null);
  };

  const onMouseMove = (e: any) => {
    if (dragStart) {
      setDragEnd(getMouseOffset(e, selectionRef.current));
    }
  };

  useEffect(() => {
    const current = selectionRef.current;
    if (current) {
      current.addEventListener('mousedown', onMouseDown);
      current.addEventListener('mouseup', onMouseUp);
      current.addEventListener('mousemove', onMouseMove);
      return () => {
        current.removeEventListener('mousedown', onMouseDown);
        current.removeEventListener('mouseup', onMouseUp);
        current.removeEventListener('mousemove', onMouseMove);
      };
    }
  }, [selectionRef, dragStart]);

  return {
    pendingSelection: dragStart && dragEnd && { start: dragStart, end: dragEnd },
  };
};

This pretty much works as expected. The problem is when I'm using this hook in another component like so:

...
const selectionRef = useRef<HTMLDivElement>();
const { pendingSelection } = useDrag(selectionRef);

return (
  <div className="selection" ref={selectionRef}></div>
  {pendingSelection && (
    <div
      style={{
        height: pendingSelection.end.y - pendingSelection.start.y,
        width: pendingSelection.end.x - pendingSelection.start.x,
      }}
    ></div>
);
...

It seems like the 'mousemove' event occurs so frequently that it is actually slowing down the rendering process (to the point of temporarily freezing the page).

I've seen react packages that handle drag events like this with very smooth updates. I know I could switch to one of these, but would really rather understand what I am doing wrong here and avoid a dependency for such a small use case.

Would be happy to provide more code or a working example. Any information appreciated!

  • Shouldn't you remove the listeners in unmount? – pritam Jul 30 '21 at 05:46
  • Try to use `useRef` to store data beacause the `useState` re-render the component on every change. `const data = useRef(null)` than you can call `data.current` where you need. `useRef` hold the key in the DOM and won't re-render the component. – Dániel Boros Jul 30 '21 at 08:06

0 Answers0