Long Press Event in React Triggering Unintended Click Event on Second Press

I’m currently stuck on an issue and could use some advice.

I’m not a fan of drag-and-drop from a UX perspective, so I implemented a long press instead. This long press reveals some additional buttons between elements where you can move the item. I’ve also added a click-anywhere-on-the-document feature to cancel the operation.

The problem arises when I try to use the long press twice in a row. Here’s the scenario: I long-press on an item (button), and the long press is triggered. Then, I decide to move another element with a second mouse down and hold. While this works, the document click event listener is fired afterward, which prevents the second action from occurring smoothly.

Here’s my code snippet:

The issue seems to be with document.removeEventListener(‘click’, documentClickRef.current); in the longPress function. It appears that the event listener is not being removed correctly, either because the method reference is not the same or because the browser doesn’t cancel the event listener once the mouse is down. I’ve tried using a clickAllowed ref, but clickAllowed.current is still true for some reason.

Any insights or solutions would be greatly appreciated!


const clickAllowed = useRef<boolean>(false);

const isLongPress = useRef<boolean>(false);

const documentClickRef = useRef(() => {
  if (!clickAllowed.current) {
    return;
  }
  document.removeEventListener('click', documentClickRef.current);
  setTimeout(() => {
    dispatch(formItemMoving(false));
  }, 0);
});

const windowMouseUpRef = useRef(() => {
  window.removeEventListener('mouseup', windowMouseUpRef.current);
  setTimeout(() => {
    isLongPress.current = false;
    document.addEventListener('click', documentClickRef.current);
    clickAllowed.current = true;
  }, 0);
});

const longPress = useLongPress(() => {
  isLongPress.current = true;
  dispatch(formItemMoving(true));
  document.removeEventListener('click', documentClickRef.current);
  clickAllowed.current = false;
  window.addEventListener('mouseup', windowMouseUpRef.current);
});

useEffect(() => {
  return () => {
    window.removeEventListener('mouseup', windowMouseUpRef.current);
    document.removeEventListener('click', documentClickRef.current);
  };
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

...

<button
    onClick={() => {
        if (!isLongPress.current) {
            dispatch(formSetItemModal(...));
        }
    }}
    {...longPress()}
></button>