Implementing a useLongPress Hook in React
Creating a reusable useLongPress hook in React allows developers to easily detect and respond to long press events on interactive elements. This is particularly useful for mobile applications or scenarios where a longer touch signifies a different action than a simple tap (e.g., opening a context menu, triggering a delete confirmation). This challenge asks you to implement such a hook, providing a clean and functional way to handle long press gestures.
Problem Description
You are tasked with creating a custom React hook called useLongPress. This hook should accept a delay (in milliseconds) as an argument and return an object containing the following functions:
onLongPress: A function that should be attached as an event handler to the element you want to monitor for long presses.onPressEnd: A function to be called when the press is released.onPressStart: A function to be called when the press begins.cancelLongPress: A function to immediately cancel any ongoing long press timer.
The hook should:
- Start a timer: When
onPressStartis called, start a timer with the provided delay. - Execute
onLongPress: If the timer completes (the user holds their finger down for the specified delay), call the providedonLongPresscallback function. - Cancel the timer: When
onPressEndis called (the user lifts their finger), cancel the timer, preventing theonLongPresscallback from being executed. - Provide callbacks:
onPressStartandonPressEndshould be called immediately when the press starts and ends, respectively, regardless of whether the long press delay is met. - Handle multiple presses: The hook should be able to handle multiple long press events without interference.
Examples
Example 1:
Input: delay = 500ms, onLongPress = () => console.log("Long press detected!")
- User presses the element.
onPressStartis called. - User holds the press for 500ms.
onLongPressis called. - User releases the press.
onPressEndis called.
Example 2:
Input: delay = 250ms, onLongPress = () => alert("Context Menu"), onPressStart = () => console.log("Press started"), onPressEnd = () => console.log("Press ended")
- User presses the element.
onPressStartis called. - User holds the press for 100ms.
- User releases the press.
onPressEndis called.onLongPressis not called.
Example 3: (Edge Case - Rapid Press and Release)
Input: delay = 1000ms, onLongPress = () => console.log("Very long press!")
- User presses the element.
onPressStartis called. - User releases the press after 200ms.
onPressEndis called.onLongPressis not called.
Constraints
- The delay must be a positive integer (greater than 0).
- The
onLongPress,onPressStart, andonPressEndcallbacks should be functions. - The hook should not cause memory leaks. Ensure proper cleanup of timers.
- The hook should work correctly in various React component types (functional components, class components).
- The hook should be performant and avoid unnecessary re-renders.
Notes
- Consider using
setTimeoutandclearTimeoutto implement the timer functionality. - Use the
useEffecthook to manage the timer and its cleanup. - Think about how to handle the component unmounting while a timer is running to prevent errors.
- The
cancelLongPressfunction should be provided to allow external cancellation of the long press timer. This is useful for scenarios where you want to preempt a long press action based on other events. - Ensure that the
onPressStartandonPressEndcallbacks are always called, regardless of whether the long press delay is met. This provides consistent feedback to the user.