Search code examples
reactjsdom-eventscreate-react-app

Add a long press event in React


Is there a way to add long press event in a React web application?

I have list of addresses. On long press on any address, I want to fire event to delete that address followed by a confirm box.


Solution

  • I've created a codesandbox with a hook to handle long press and click. Basically, on mouse down, touch start events, a timer is created with setTimeout. When the provided time elapses, it triggers long press. On mouse up, mouse leave, touchend, etc, the timer is cleared.

    useLongPress.js

    import { useCallback, useRef, useState } from "react";
    
    const useLongPress = (
        onLongPress,
        onClick,
        { shouldPreventDefault = true, delay = 300 } = {}
        ) => {
        const [longPressTriggered, setLongPressTriggered] = useState(false);
        const timeout = useRef();
        const target = useRef();
    
        const start = useCallback(
            event => {
                if (shouldPreventDefault && event.target) {
                        event.target.addEventListener("touchend", preventDefault, {
                        passive: false
                    });
                    target.current = event.target;
                }
                timeout.current = setTimeout(() => {
                    onLongPress(event);
                    setLongPressTriggered(true);
                }, delay);
            },
            [onLongPress, delay, shouldPreventDefault]
        );
    
        const clear = useCallback(
            (event, shouldTriggerClick = true) => {
                timeout.current && clearTimeout(timeout.current);
                shouldTriggerClick && !longPressTriggered && onClick();
                setLongPressTriggered(false);
                if (shouldPreventDefault && target.current) {
                    target.current.removeEventListener("touchend", preventDefault);
                }
            },
            [shouldPreventDefault, onClick, longPressTriggered]
        );
    
        return {
            onMouseDown: e => start(e),
            onTouchStart: e => start(e),
            onMouseUp: e => clear(e),
            onMouseLeave: e => clear(e, false),
            onTouchEnd: e => clear(e)
        };
    };
    
    const isTouchEvent = event => {
    return "touches" in event;
    };
    
    const preventDefault = event => {
    if (!isTouchEvent(event)) return;
    
    if (event.touches.length < 2 && event.preventDefault) {
        event.preventDefault();
    }
    };
    
    export default useLongPress;
    

    To use the hook, App.js

    import useLongPress from "./useLongPress";
    
    export default function App() {
    
        const onLongPress = () => {
            console.log('longpress is triggered');
        };
    
        const onClick = () => {
            console.log('click is triggered')
        }
    
        const defaultOptions = {
            shouldPreventDefault: true,
            delay: 500,
        };
        const longPressEvent = useLongPress(onLongPress, onClick, defaultOptions);
    
        return (
            <div className="App">
                <button {...longPressEvent}>use  Loooong  Press</button>
            </div>
        );
    }
    

    Older answer for class components:

    You can use MouseDown, MouseUp, TouchStart, TouchEnd events to control timers that can act as a long press event. Check out the code below

    class App extends Component {
      constructor() {
        super()
        this.handleButtonPress = this.handleButtonPress.bind(this)
        this.handleButtonRelease = this.handleButtonRelease.bind(this)
      }
      handleButtonPress () {
        this.buttonPressTimer = setTimeout(() => alert('long press activated'), 1500);
      }
      
      handleButtonRelease () {
        clearTimeout(this.buttonPressTimer);
      }
    
      render() {
        return (
          <div 
              onTouchStart={this.handleButtonPress} 
              onTouchEnd={this.handleButtonRelease} 
              onMouseDown={this.handleButtonPress} 
              onMouseUp={this.handleButtonRelease} 
              onMouseLeave={this.handleButtonRelease}>
            Button
          </div>
        );
      }
    }