Skip to main content
react-md

useHoverMode

function useHoverMode(
  options: ControlledHoverModeOptions | UncontrolledHoverModeOptions
): HoverModeImplementation;

The useHoverMode hook is used to implement an immediate hover state after hovering related elements for a short duration. The main use-case is for showing tooltips immediately after hovering another tooltipped element.

This relies on creating a context provider using useHoverModeProvider to link related elements together.

Example Usage

See the useTooltip and TooltipHoverModeProvider for a real world example.

import { type MouseEvent } from "react";

import {
  type CustomHoverContext,
  useCustomHoverContext,
} from "./useCustomHoverContext.jsx";

interface CustomHoverModeImplementation {
  onMouseEnter: <E extends HTMLElement>(event: MouseEvent<E>) => void;
  onMouseLeave: <E extends HTMLElement>(event: MouseEvent<E>) => void;
}

function useCustomHoverMode(): CustomHoverModeImplementation {
  const {
    animatedOnceRef,
    hoverTimeoutRef,
    leaveTimeoutRef,
    enableHoverMode,
    disableHoverMode,
    startDisableTimer,
    clearDisableTimer,
  } = useCustomHoverContext();
  const {
    visible,
    setVisible,
    startShowFlow,
    startHideFlow,
    clearVisibilityTimeout,
  } = useHoverMode({
    hoverTimeout,
    hoverTimeoutRef,
    leaveTimeout,
    leaveTimeoutRef,
    enableHoverMode,
    disableHoverMode,
    startDisableTimer,
    clearDisableTimer,
  });

  return {
    onMouseEnter(event) {
      startShowFlow(event.currentTarget.id);
    },
    onMouseLeave(event) {
      startHideFlow();
    },
  };
}

Parameters

export type HoverModeOptions =
  | ControlledHoverModeOptions
  | UncontrolledHoverModeOptions;

export interface ControlledHoverModeOptions
  extends HoverModeConfigurationOptions {
  setVisible: UseStateSetter<boolean>;
}

export interface UncontrolledHoverModeOptions
  extends HoverModeConfigurationOptions {
  defaultVisible?: UseStateInitializer<boolean>;
}

export interface HoverModeConfigurationOptions extends SimpleHoverModeContext {
  /** @defaultValue `false` */
  disabled?: boolean;

  /**
   * This can be used to override the `HoverModeContext`'s hover time.
   */
  hoverTimeout?: number;

  /**
   * This can be used to override the `HoverModeContext`'s leave time.
   */
  leaveTimeout?: number;
}
export interface SimpleHoverModeContext {
  /**
   * @example Main Usage
   * ```ts
   * onMouseEnter(event) {
   *   const hoverTimeout = hoverTimeoutRef.current;
   *   if (typeof hoverTimeout !== "number" || mode === "touch") {
   *     return;
   *   }
   *
   *   const { id } = event.currentTarget;
   *   clearDisableTimer();
   *   window.clearTimeout(visibilityTimeout.current);
   *   visibilityTimeout.current = window.setTimeout(() => {
   *     enableHoverMode(id);
   *     setVisible(true);
   *   }, hoverTimeout);
   * }
   * ```
   */
  hoverTimeoutRef: NonNullRef<number | undefined>;

  /**
   * @example Main Usage
   * ```ts
   * onMouseLeave() {
   *   if (mode === "touch") {
   *     return
   *   }
   *
   *   startDisableTimer();
   *   window.clearTimeout(visibilityTimeout.current);
   *   visibilityTimeout.current = window.setTimeout(() => {
   *     setVisible(false)
   *   }, leaveTimeoutRef.current);
   * }
   * ```
   */
  leaveTimeoutRef: NonNullRef<number>;

  /**
   * When this is called, the {@link hoverTimeoutRef} will be set to `0` and the
   * {@link HoverModeContext.activeId} will be set to this `activeId` value.
   *
   * @see {@link hoverTimeoutRef} for an example.
   */
  enableHoverMode: (activeId: string) => void;

  /**
   * Disables all hover mode behavior by clearing all timeouts and resetting
   * internal state.
   */
  disableHoverMode: () => void;

  /**
   * @see {@link leaveTimeoutRef} for an example.
   */
  startDisableTimer: () => void;

  /**
   * @see {@link hoverTimeoutRef} for an example.
   */
  clearDisableTimer: () => void;
}

Returns

An object with the following definition:

export interface HoverModeImplementation {
  // `visible` and `setVisible` will be defined if the `setVisible` option was not provided
  visible?: boolean;
  setVisible?: UseStateSetter<boolean>;
  startShowFlow: (id?: string | MouseEvent) => void;
  startHideFlow: () => void;
  clearVisibilityTimeout: () => void;
}

See Also