Hone logo
Hone
Problems

Implement a useDoubleClick React Hook

Create a custom React hook, useDoubleClick, that simplifies handling double-click events on an element. This hook should abstract away the logic for tracking clicks and identifying when a double-click has occurred, making it easier to implement double-click functionalities in your React applications.

Problem Description

Your task is to implement a custom React hook named useDoubleClick. This hook will take a callback function as an argument, which will be executed only when a double-click event is detected on the element where the hook's returned event handler is attached.

Key Requirements:

  • The hook should accept a single argument: callback (a function that takes no arguments and returns void).
  • The hook should return a single value: an event handler function ((event: React.MouseEvent<Element>) => void). This function should be attached to the onClick prop of a DOM element.
  • The callback function should only be invoked when the onClick handler is triggered twice in quick succession within a predefined time window.
  • The hook should manage the internal state required to track clicks and their timestamps.
  • The hook should be written in TypeScript.

Expected Behavior:

When the returned onClick handler is invoked:

  1. The hook checks if a previous click occurred recently.
  2. If a previous click occurred within the double-click time threshold, and the current click is also within that threshold of the previous one, the callback function is executed.
  3. If it's the first click or if the time between clicks exceeds the threshold, the hook records the current click's timestamp and waits for the next click.
  4. Any clicks after a successful double-click should reset the state, waiting for a new sequence.

Edge Cases to Consider:

  • Rapid successive clicks: Ensure the logic correctly distinguishes between single clicks, double clicks, and more than two rapid clicks.
  • Timeouts: What happens if a click occurs, but the second click doesn't happen within the time threshold? The hook should reset and wait for a new first click.
  • Unmounting: Ensure no memory leaks occur if the component using the hook unmounts before a double-click is registered.

Examples

Example 1: Basic Double Click

Imagine a button that logs a message to the console when double-clicked.

  • Input to hook: callback = () => console.log('Double clicked!');
  • Usage:
    const handleClick = useDoubleClick(() => console.log('Double clicked!'));
    
    return (
      <button onClick={handleClick}>Double Click Me</button>
    );
    
  • Behavior:
    1. User clicks the button once. Nothing happens immediately.
    2. User clicks the button again within the time threshold.
    3. Output: 'Double clicked!' is logged to the console.
    4. If the user waits too long between the first and second click, the first click is effectively ignored for the purpose of a double-click, and a new sequence starts.

Example 2: State Update on Double Click

A component that toggles a boolean state when an element is double-clicked.

  • Input to hook: callback = () => setIsToggled(prev => !prev);
  • Usage:
    const [isToggled, setIsToggled] = useState(false);
    const handleDoubleClick = useDoubleClick(() => setIsToggled(prev => !prev));
    
    return (
      <div onClick={handleDoubleClick} style={{ padding: '20px', border: '1px solid black' }}>
        Double click this area. Current state: {isToggled ? 'ON' : 'OFF'}
      </div>
    );
    
  • Behavior:
    1. User clicks the div once. Nothing visible changes.
    2. User double-clicks the div within the time threshold.
    3. Output: The text "Current state: ON" or "Current state: OFF" visibly updates.

Example 3: Handling Consecutive Double Clicks

Demonstrates how consecutive double clicks are handled.

  • Input to hook: callback = () => console.log('Double click detected!');
  • Usage: Same as Example 1.
  • Behavior:
    1. User double-clicks the button.
    2. Output: 'Double click detected!' is logged.
    3. Immediately after the first double-click is processed, the hook resets its internal state.
    4. If the user double-clicks the button again very quickly, it will be treated as a new, separate double-click event.

Constraints

  • The double-click time threshold is a configurable internal detail of the hook, but a reasonable default (e.g., 300-500 milliseconds) should be assumed for implementation. For the purpose of this challenge, you can hardcode this value within the hook.
  • The hook should not rely on any external libraries.
  • The solution must be in TypeScript.
  • The callback function should be stable and not be recreated on every render if the component itself re-renders unnecessarily. Consider using useCallback within the hook.

Notes

  • Consider using useRef to store the timer ID and the last click timestamp, as these values need to persist across renders without causing re-renders themselves.
  • Remember to clear any active timers when the component unmounts to prevent memory leaks. The useEffect hook with a cleanup function is ideal for this.
  • The event handler returned by the hook will receive a React.MouseEvent object, which you can use if your callback needs access to event details, though the core requirement is to trigger the callback itself.
Loading editor...
typescript