ReactJS: Handle Single-Click and DoubleClick at the same time (Click Manager)

Sometimes you need to have both single-click and double-click handler on a component. You don’t want to single-click-handler run when you click twice on a component! So what should we do?

(You can check the codepen demo at the end of the page.)

JavaScript Timeout

The Idea is simple: we need a timeout for a single-click handler, to run it with a little delay like 280ms. After a click, we will wait until 280ms, and if there were no other clicks, run single-click-handler. If in that 280ms was another click, we count it as double-click!

This is how we can define a timeout and clear it:

const delay = 1000; // ms
const timeoutId = setTimeout(() => {
    console.log('timeout is ran!');
}, delay);

console.log('the timeout id is:', timeoutId);

// log: the timeout id is: 39414
// return: undefined
// log after 1 second: timeout is ran!

So if you want to stop the timeout, simply use clearTimeout function and pass timeoutId to it. like this:

const delay = 1000; // ms
const timeoutId = setTimeout(() => {
    console.log('timeout is ran!');
}, delay);
console.log('the timeout id is:', timeoutId);

// clear it
clearTimeout(timeoutId);
console.log('timeout will never run!')

// log: the timeout id is: 56878
// log: timeout will never run!

Click Manager

The render of click manager should be like:



import React, {useState} from 'react';

const ClickManager = ({
  handleSingleClick,
  handleDoubleClick,
  children,
  ...props
}) => {
  // logic codes here ...

  return (
    <div
      onClick={onSingleClick}
      onDoubleClick={onDoubleClick}
      {...props} 
    >
      {children} 
    </div>
  );
}

And the handlers:

import React, {useState} from 'react';

const ClickManager = ({
  handleSingleClick,
  handleDoubleClick,
  children,
  ...props
}) => {
  const [clickCount, setClickCount] = useState(0);
  const [singleClickTimer, setSingleClickTimer] = useState(null);
  const delay = 280; // in ms

  const onSingleClick = (e) => {
    console.log('-------- is on click');

    // increase click count
    const currentClickCount = clickCount+1;
    setClickCount(currentClickCount); 

    if (currentClickCount === 1) {
        
      const timeoutId = setTimeout(() => {
        // reset click count
        setClickCount(0);

        // handle single click if handler is passed
        if(handleSingleClick) handleSingleClick(e);
      }, delay);
      setSingleClickTimer(timeoutId); // save timeoutId for being able to cancel it

    } else {
      // click count is 2, it will be handled by `onDoubleClick` event, so do nothing!

    }
  };


  const onDoubleClick = (e) => {
    console.log('-------- is on DOUBBLE click');

    // -- first of all, cancel singleClick timeout and reset cilckCount
    clearTimeout(singleClickTimer);
    setClickCount(0);

    // handle double click if handler is passed
    if(handleDoubleClick) handleDoubleClick(e);
  };

  return (
    // the render of componnent ... 
  );
}

And how we use it:

const Button = () => {
  // component logic codes here ...

  return (
    <ClickManager
      handleSingleClick={() => {/* handle single click */}}
      handleDoubleClick={() => {/* handle double click */}}
    >
      <button>
        single Click {singleClickCount} times, double click {doubleClickCount} times
      </button>
    </ClickManager>
  );
};

That’s all! This is the result:

See the Pen Untitled by Ali Amini (@AliAmini) on CodePen.

Leave a Reply

Your email address will not be published.