/** * useUndo Hook * Simplified undo/redo wrapper for any state value * * Features: * - Generic typing for any value type * - Simple undo/redo operations with boundary checks * - Reset to initial or specific value * - Query undo/redo availability * - Lightweight and performant * - Useful as alternative to useStateWithHistory for simpler use cases * * @example * const { value, undo, redo, reset, canUndo, canRedo } = useUndo('initial value') * * const handleChange = (newValue: string) => { * // Setting a new value automatically enables undo * setValue(newValue) * } * *
*

Current: {value}

* * * * *
* * @example * // With text editor * const { value: text, undo, redo, canUndo, canRedo } = useUndo('') * * return ( *
* setValue(newText)} * /> * *
* ) * * @example * // Track both past and future for undo/redo * const { value, undo, redo } = useUndo({ x: 0, y: 0 }) * // Modify canUndo/canRedo based on history tracking */ import { useState, useCallback } from 'react' export interface UseUndoReturn { /** Current value */ value: T /** Set new value (stores current value for undo) */ setValue: (value: T | ((prev: T) => T)) => void /** Undo to previous value */ undo: () => void /** Redo to next value */ redo: () => void /** Reset to initial value */ reset: (value?: T) => void /** Check if undo is available */ canUndo: boolean /** Check if redo is available */ canRedo: boolean } /** * Hook for managing undo/redo operations on any value * @template T - The type of the value * @param initialValue - Initial value * @returns Object containing value and undo/redo operations */ export function useUndo(initialValue: T): UseUndoReturn { // past[past.length - 1] is the value before current // future[0] is the value after current const [past, setPast] = useState([]) const [present, setPresent] = useState(initialValue) const [future, setFuture] = useState([]) const setValue = useCallback((newValue: T | ((prev: T) => T)) => { const valueToStore = newValue instanceof Function ? newValue(present) : newValue setPast((prevPast) => [...prevPast, present]) setPresent(valueToStore) setFuture([]) }, [present]) const undo = useCallback(() => { if (past.length === 0) return setPast((prevPast) => { const newPast = [...prevPast] const previousValue = newPast.pop() if (previousValue !== undefined) { setFuture((prevFuture) => [present, ...prevFuture]) setPresent(previousValue) } return newPast }) }, [present]) const redo = useCallback(() => { if (future.length === 0) return const nextValue = future[0] setPast((prevPast) => [...prevPast, present]) setFuture((prevFuture) => prevFuture.slice(1)) setPresent(nextValue) }, [present, future]) const reset = useCallback((value: T = initialValue) => { setPast([]) setPresent(value) setFuture([]) }, [initialValue]) const canUndo = past.length > 0 const canRedo = future.length > 0 return { value: present, setValue, undo, redo, reset, canUndo, canRedo, } }