Hone logo
Hone
Problems

Implement a useHover React Hook in TypeScript

This challenge asks you to create a custom React hook, useHover, that tracks whether a DOM element is currently being hovered over. This is a common pattern used for implementing UI effects like tooltips, dropdowns, or visual feedback on interactive elements.

Problem Description

Your task is to implement a reusable custom React hook called useHover that accepts a React RefObject pointing to a DOM element. The hook should return a boolean value indicating whether the mouse is currently hovering over the referenced element.

Key Requirements:

  1. Hook Signature: The hook should have the following signature:

    function useHover<T extends HTMLElement>(ref: React.RefObject<T>): boolean;
    
    • It accepts a React.RefObject that targets an HTML element (T extends HTMLElement).
    • It returns a boolean: true if hovered, false otherwise.
  2. Event Handling: The hook needs to attach and detach event listeners to the referenced DOM element.

    • On mouseenter (or mouseover), it should set the hover state to true.
    • On mouseleave (or mouseout), it should set the hover state to false.
  3. State Management: The hook should manage its internal state to keep track of the hover status.

  4. Cleanup: It's crucial to clean up the event listeners when the component unmounts or the ref changes to prevent memory leaks.

Expected Behavior:

When a component uses useHover with a ref to an element, the returned boolean should accurately reflect whether the user's mouse pointer is currently over that element.

Edge Cases to Consider:

  • Initial State: The hook should initialize with false (not hovered).
  • Ref Changes: If the ref object itself is re-assigned (though this is less common for DOM refs), the hook should adapt.
  • Element Not Found: If the ref.current is null (e.g., the element hasn't mounted yet), the hook should gracefully handle this without errors.

Examples

Example 1:

Component Usage:

import React, { useRef } from 'react';
import { useHover } from './useHover'; // Assuming useHover is in this file

function MyComponent() {
  const buttonRef = useRef<HTMLButtonElement>(null);
  const isHovered = useHover(buttonRef);

  return (
    <button ref={buttonRef} style={{ padding: '10px', cursor: 'pointer' }}>
      {isHovered ? 'Hovering!' : 'Hover over me'}
    </button>
  );
}

Expected Output (based on user interaction):

  • Initial Render / Mouse not over button: The button displays "Hover over me".
  • Mouse enters button area: The button text changes to "Hovering!".
  • Mouse leaves button area: The button text reverts to "Hover over me".

Explanation: The useHover hook correctly detects when the mouse enters and leaves the buttonRef element and provides the isHovered boolean to control the button's text.

Example 2:

Component Usage:

import React, { useRef } from 'react';
import { useHover } from './useHover';

function DivWithTooltip() {
  const divRef = useRef<HTMLDivElement>(null);
  const isHovered = useHover(divRef);

  return (
    <div
      ref={divRef}
      style={{
        width: '100px',
        height: '50px',
        backgroundColor: 'lightblue',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        cursor: 'pointer',
      }}
    >
      {isHovered ? 'Show Tooltip' : 'My Element'}
    </div>
  );
}

Expected Output (based on user interaction):

  • Initial Render / Mouse not over div: The div displays "My Element".
  • Mouse enters div area: The div text changes to "Show Tooltip".
  • Mouse leaves div area: The div text reverts to "My Element".

Explanation: Similar to Example 1, the useHover hook provides the hover state, which is used here to conditionally render text within a div.

Constraints

  • The solution must be written in TypeScript.
  • The hook should be a pure function that adheres to React hook rules (e.g., called at the top level of a functional component or another hook).
  • Event listeners must be properly added and removed to avoid memory leaks.
  • The hook should handle cases where the referenced element might not be immediately available (ref.current could be null).

Notes

  • Consider using useRef and useEffect hooks within your custom hook.
  • Pay close attention to the dependencies array in useEffect to ensure correct cleanup and re-attachment of event listeners if the ref itself were to change (though typically DOM refs are stable).
  • The event names mouseenter and mouseleave are generally preferred over mouseover and mouseout for simple hover detection as they don't bubble in the same way and can prevent redundant event firings when hovering over child elements.
Loading editor...
typescript