Search code examples
javascriptreactjsreact-hookslong-press

react long press event for stopwatch


I've been struggling a lot with this question. Before creating this post here, I've searched throughout the community and I've seen so many solutions for long press events in React, but everyone of them solves just for a click button event.

I'm creating a stopwatch for Rubik's myself. What I need to do is the following steps:

  1. The user must hold spacebar for two seconds to be "good to go";
  2. Once the user is "good to go", at the moment he releases the spacebar, the stopwatch starts;
  3. Once he's done with the Rubik's, he presses the spacebar again to stop the stopwatch.

P.S-1 : If the stopwatch is not running and the user just presses the spacebar (does not hold it), nothing should happen! The stopwatch only starts if the user holds the spacebar for at least two seconds and then releases it.

Now, my problem is that all the events (start and stop) I should be looking at is under the "keydown" and "keyup" listeners in JavaScript. I've tried so many different solutions to manage the timeout native function in JS, but as I said, I should not do anything if the stopwatch is stopped and the user just presses the spacebar.

I've tried to use useState and useRef hook to control the timeout and to check if the spacebar was just pressed or it was long pressed. And again, my problem is not knowing how to manage it once these two events are under the "keydown" listener.

I hope you guys can help me, it would mean a lot!

Thank you in advance.

P.S-2: I'm using TypeScript!


Solution

  • Edit: I forgot that keydown fires multiple times when it's held, check out the example below, just need to check whether the key is a "repeat" key. I had to use useRef to get it working but should be a good foundation for the rest of your logic:

    const timestamp = useRef(0);
    
    const spacebarPress = (e) => {
      // exit if the key is not spacebar or is the same key as last pressed
      if (e.keyCode !== 32 || e.repeat) return; 
      timestamp.current = Date.now();
    }
    
    const spacebarRelease = (e) => {
      if (e.keyCode !== 32) {
        return; // exit if the key is not spacebar
      }
      const pressed = timestamp.current, released = Date.now();
      if (released - pressed < 2000)
        return; // less than 2 seconds have passed since pressing
      // ... your logic here, spacebar was pressed for two seconds.
    }
    
    

    I generally tend to avoid setTimeout where possible, IMO it's not very reliable and a pain to debug.

    Should get you most of the way, just add a different key down/up event while the timer is running if e.repeat stops the timer from being paused again.