All files / react-classic/src history.ts

100% Statements 35/35
100% Branches 7/7
100% Functions 1/1
100% Lines 35/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 681x 1x 1x   1x                           1x 11x     11x   11x     11x 11x   11x   10x   10x 10x 11x   1x     1x 1x 1x   11x 11x 11x   11x   10x   10x   10x 9x 9x 9x   9x 10x 10x 11x   11x 11x  
import { history, type HistoryOptions, type HistoryState, softEqual, type State } from '@anchorlib/core';
import { useEffect, useMemo, useRef } from 'react';
import { CLEANUP_DEBOUNCE_TIME } from './constant.js';
 
import { useMicrotask } from './hooks.js';
 
export type HistoryRef = {
  history: HistoryState;
  options?: HistoryOptions;
};
/**
 * A React hook that provides history management for a given state.
 *
 * @template T - The type of the state extending from State
 * @param state - The initial state to track history for
 * @param options - Optional history configuration options
 * @returns The history state object containing current state, history methods, and navigation functions
 */
export function useHistory<T extends State>(state: T, options?: HistoryOptions): HistoryState {
  const [cleanup, cancelCleanup] = useMicrotask(CLEANUP_DEBOUNCE_TIME);
 
  // Create reference map to hold the history states that being used in the current component.
  const historyRef = useRef(new Map<State, HistoryRef>()).current;
  // Create reference to hold the active history state.
  const currentRef = useRef<HistoryState>(null);
 
  // Use memo to create or switch between history states when the state or options changes.
  const historyState = useMemo(() => {
    let current = historyRef.get(state) as HistoryRef;
 
    if (!current) {
      // Initialize new history state if the state is not in the reference map.
      const newHistory = history(state, options);
 
      current = { history: newHistory, options };
      historyRef.set(state, current);
    } else if (options && !softEqual(options, current.options)) {
      // Cleanup the existing history state if the options have changed before re-creating.
      current.history.destroy();
 
      // Re-create the history state if the options have changed.
      current.history = history(state, options);
      current.options = options;
    }
 
    currentRef.current = current.history;
    return currentRef.current;
  }, [state, options]);
 
  useEffect(() => {
    // Prevent cleanup if the component is unmounted and remounted quickly (e.g., in Strict Mode).
    cancelCleanup();
 
    return () => {
      // Cleanup the history when the component is truly unmounted.
      cleanup(() => {
        for (const ref of historyRef.values()) {
          ref.history.destroy();
        }
 
        historyRef.clear();
      });
    };
  }, []);
 
  return historyState;
}