3

I'm trying to make a draggable React component, and the code always manages to reach the 'removing listeners' block, but afterwards the component continues sticking to my mouse, so definitely I've screwed up the removing listeners step, though I'm not sure how--surely the two functions I've passed into document.add/removeEventListener are the same?

useEffect(() => {
            if (dragging && !prevDragging) {
                document.addEventListener("mousemove", onMouseMove)
                document.addEventListener("mouseup", onMouseUp)
            } else if (!dragging && prevDragging) {
                console.log('removing listeners')
                document.removeEventListener("mousemove", onMouseMove)
                document.removeEventListener("mouseup", onMouseUp)
                }
            }
        )

 const onMouseMove = (e) => {
            if (!dragging) return;
            setPos({
                x: e.pageX - rel.x,
                y: e.pageY - rel.y
            });
        e.stopPropagation();
        e.preventDefault();
    }

const onMouseUp = (e) => {
        setDragging(false);
        e.stopPropagation();
        e.preventDefault();
    }
shadowstuff
  • 31
  • 1
  • 2

2 Answers2

3

Just use Reacts useCallback function so React will memorize that the functions are same.

const onMouseMove = React.useCallback((e) => {
  if (!dragging) return;
  setPos({
    x: e.pageX - rel.x,
    y: e.pageY - rel.y,
  });
  e.stopPropagation();
  e.preventDefault();
}, []);

const onMouseUp = React.useCallback((e) => {
  setDragging(false);
  e.stopPropagation();
  e.preventDefault();
}, []);
Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
Hyzyr
  • 568
  • 4
  • 13
0

On each render of React component a new function is created. So if you check in your browser's developer tools for the event listeners you'll see a couple of them added after each render. So even if you remove the latest event listener, the old one's are still there.

So essentially what I am saying is that the function:-

const onMouseMove = (e) => {
            if (!dragging) return;
            setPos({
                x: e.pageX - rel.x,
                y: e.pageY - rel.y
            });
        e.stopPropagation();
        e.preventDefault();
    }

on first render is not same as

const onMouseMove = (e) => {
            if (!dragging) return;
            setPos({
                x: e.pageX - rel.x,
                y: e.pageY - rel.y
            });
        e.stopPropagation();
        e.preventDefault();
    }

on second render and so on. They are logically equivalent but do not refer to the same function.

Lakshya Thakur
  • 8,030
  • 1
  • 12
  • 39
  • So the way I defined useEffect, it should only call addEventListener once before removeEventListener is called -- but you're saying that it doesn't matter, because on each render of the component the functions are different? How then do I refer to the same function? Thanks! – shadowstuff Jun 01 '20 at 15:39