score:2

Accepted answer

you should wrap that callback task into a reducer and trigger the timeout as an effect. yes, that makes things certainly more complicated (but it's "best practice"):

  const button = ({ clickhandler, clickholdhandler, children }) => {
     const holdtime = 1000;
     const [holding, pointer] = usereducer((state, action) => {
        if(action === "down") 
           return { holding: true, time: date.now()  };
        if(action === "up") {
          if(!state.holding)
              return { holding: false };
          if(state.time + holdtime > date.now()) {
                clickhandler();
          } else {
                clickholdhandler();
          }
          return { holding: false };
        }
        if(action === "leave")
          return { holding: false };
     }, { holding: false, time: 0 });

     useeffect(() => {
       if(holding.holding) {
         const timer = settimeout(() => pointer("up"), holdtime - date.now() + holding.time);
         return () => cleartimeout(timer);
       }
     }, [holding]);

     return (
       <button
         onmousedown={() => pointer("down")}
         onmouseup={() => pointer("up")}
         onmouseleave={() => pointer("leave")}
       >
         {children}
        </button>
    );
  };

working sandbox: https://codesandbox.io/s/7yn9xmx15j


as a fallback if the reducer gets too complicated, you could memoize an object of settings (not best practice):

 const state = usememo({
   isholding: false,
   holdstarttime: undefined,
 }, []);

 // somewhere
 state.isholding = true;

Related Query

More Query from same tag