1

Let's say I have a responsive ReactJS component (Outer) that does something when I click it. Inside the larger component I want a single button that does something special when I double-click it.

export default function FancyDoubleClickHandler() {
  
  function handleOuterClick() {
    console.log("outer single click")
  }
  
  function handleInnerClick() { 
    ??? 
  }

  return (
    <div id="outer" onClick={handleOuterClick}>
       <p>Click on me to do outer click</p>
       <button id="inner" onDoubleClick={handleInnerClick}>
         <p>
           Click on me to do outer click -- OR --
           Double-click on me to do special inner click thing only
         </p>
       </button>
    </div>
  )
}

As written, the onDoubleClick event does not prevent the outer onClick event from firing (twice). That basic problem is solved in this SO question, by avoiding onDoubleClick and instead using a unified click handler that fires single clicks only after a timeout has expired:

function handleAllClicks(event) {
  clearTimeout(timer);
  
  if (event.detail === 1) {
    timer = setTimeout(() => {
      console.log("SINGLE CLICK");
    }, SOME_DOUBLECLICK_DELAY)

  } else if (event.detail === 2) {
    console.log("DOUBLE CLICK");
  }
}

But in that answer, there's no need to stop event propagation, so a double click on the inner button still propagates two click events to handleOuterClick. In my case, I need to suppress those outer events in the case of a double-click.

In (modern) ReactJS, how can I achieve an architecture in which an inner element intercepts and handles a double-click, but propagates a single click upward?

Sasgorilla
  • 2,403
  • 2
  • 29
  • 56

1 Answers1

2

you cannot stop invocation of onClick event of parent element in onDoubleClick event of inner element. to stop events propagation you should call event.stopPropagation() in the inner element onClick event handler and simulate double click in it. i hope this works for you:

function FancyDoubleClickHandler() {
  let timer
  function handleOuterClick() {
    console.log("outer single click")
  }
  function handleInnerClick(e) { 
    e.stopPropagation(); 
    clearTimeout(timer)
    if (e.detail === 1) {
        timer = setTimeout(() => {
         handleOuterClick()
        }, 250);
    }
    if(e.detail === 2) {
      console.log("inner double click");
    }
  }

  return (
    <div id="outer" onClick={handleOuterClick}>
       <p>Click on me to do outer click</p>
       <button id="inner" onClick={handleInnerClick}>
         <p>
           Click on me to do outer click -- OR --
           Double-click on me to do special inner click thing only
         </p>
       </button>
    </div>
  )
}
  • This is promising -- however, this solution means that I have to know about any click handlers higher up in the stack (possibly in other components). (In my actual use case the outer element is a Material-UI accordion component that has its own built-in click handler; i.e., I don't actually have access to `outerClickHandler`.) So I'm still looking for a solution that propagates the original event. – Sasgorilla Jul 17 '21 at 23:21