0

this is probably really simple but anyone know how to make a long press button in react. Like a button where if you press and hold for 2 seconds it is "clicked", otherwise it is not. Is there a react mouse event for this? Maybe I use onMouseDown event in some clever way?

Ratul Alam
  • 11
  • 5

2 Answers2

0

One of the hack you can try is that you note the time when 'onMouseDown' event is fired and also note the time when 'onMouseUp' is fired.

If the difference between these times is greater than equal to 2 seconds, you can perform the action you want.

You must write logic to check the time difference and the code you want to execute, in method which will get executed when 'onMouseUp' event is fired.

delay = 2000;
startPress = null;

function mouseDown() {
    startPress = Date.now();
}

function mouseUp() {
    if(Date.now() - startPress > delay)
    {
        // your code
    }
}
  • Hi, for some reason this is not working. I do a console log inside the if statement to see If it is working and the message never pops up. It does not really make sense. – Ratul Alam Aug 02 '21 at 15:15
  • I am not sure how to show you what I coded, should I post it as an answer? – Ratul Alam Aug 02 '21 at 15:15
  • It seems for me that startPress is always null but I update it on mouseDown. When I console.log() in mouseDown I can see the data startPress is holding but startPress is null in mouseUp. Is it a simple variable scope issue? I used const for delay and let for startPress. – Ratul Alam Aug 02 '21 at 15:20
  • @RatulAlam Yes it seems like variable scope issue. you must declare them as global to share common value with both of these methods. Hope it is working as you expected. – Shreekesh Murkar Aug 03 '21 at 05:30
0

I used the following to create a button that dynamically changes class while you hold down the button. The use of setInterval instead of setTimeout simplifies the operation, since we manage the end-count ourselves. We don't do any type of DateTime calculations, and we can actively monitor the current delay milliseconds by watching currentCount.

I'm using DaisyUI as my component framework, but any CSS classes could be used.

import { useCallback, useEffect, useRef, useState } from 'react';

export type LongPressButtonProps = {
  /** Text that will appear within the button */
  buttonText: string;
  /** Function to run once delay has been exceeded */
  onExecute: () => void;
  /** How long should be button be held before firing onExecute */
  delayMs: number;
  /** How frequently should the count be updated */
  refreshMs: number;
};

const LongPressButton = ({
  delayMs,
  onExecute,
  buttonText,
  refreshMs = 100,
}: LongPressButtonProps) => {
  const [mouseDown, setMouseDown] = useState(false);
  const [currentCount, setCurrentCount] = useState(0);
  const intervalRef = useRef<NodeJS.Timer>();
  const [buttonClass, setButtonClass] = useState('btn-primary');

  const onInterval = useCallback(() => {
    setCurrentCount((c) => c + refreshMs);
  }, [setCurrentCount, refreshMs]);

  useEffect(() => {
    if (mouseDown) intervalRef.current = setInterval(onInterval, refreshMs);

    if (!mouseDown && intervalRef.current) {
      clearInterval(intervalRef.current);
      setCurrentCount(0);
      setButtonClass(`btn-primary`);
    }
  }, [onInterval, delayMs, mouseDown, refreshMs]);

  useEffect(() => {
    if (currentCount > 0) setButtonClass(`btn-error`);
    if (currentCount > delayMs) {
      onExecute();
      setCurrentCount(0);
    }
  }, [currentCount, delayMs, onExecute]);

  return (
    <button
      className={`btn ${buttonClass}`}
      onMouseDown={() => setMouseDown(true)}
      onMouseUp={() => setMouseDown(false)}
      onMouseLeave={() => setMouseDown(false)}
      onTouchStart={() => setMouseDown(true)}
      onTouchEnd={() => setMouseDown(false)}
      style={{ transition: `${delayMs}ms` }}
    >
      {buttonText}
    </button>
  );
};

export default LongPressButton;
Lee Harrison
  • 2,306
  • 21
  • 32