Rooks

useUndoRedoState

About

A hook that provides state management with undo and redo capabilities. It maintains a history of state changes and allows you to navigate through them, making it perfect for features like text editors, drawing apps, or any application where users need to undo/redo their actions.

Examples

Basic counter example

import { useUndoRedoState } from "rooks";
 
export default function App() {
  const [count, setCount, controls] = useUndoRedoState(0);
 
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
      <br />
      <button onClick={controls.undo} disabled={!controls.isUndoPossible}>
        Undo
      </button>
      <button onClick={controls.redo} disabled={!controls.isRedoPossible}>
        Redo
      </button>
      <button onClick={controls.clearAll}>Clear History</button>
    </div>
  );
}

Text editor with function updater

import { useUndoRedoState } from "rooks";
 
export default function TextEditor() {
  const [text, setText, controls] = useUndoRedoState("");
 
  const handleAddText = (newText) => {
    setText(prevText => prevText + newText);
  };
 
  const handleClearText = () => {
    setText("");
  };
 
  return (
    <div>
      <textarea
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Start typing..."
        rows={4}
        cols={50}
      />
      <br />
      <button onClick={() => handleAddText(" Hello!")}>Add Hello</button>
      <button onClick={handleClearText}>Clear</button>
      <br />
      <button onClick={controls.undo} disabled={!controls.isUndoPossible}>
        Undo ({controls.isUndoPossible ? "Available" : "Not Available"})
      </button>
      <button onClick={controls.redo} disabled={!controls.isRedoPossible}>
        Redo ({controls.isRedoPossible ? "Available" : "Not Available"})
      </button>
      <br />
      <button onClick={controls.clearUndoStack}>Clear Undo History</button>
      <button onClick={controls.clearRedoStack}>Clear Redo History</button>
    </div>
  );
}

Advanced example with limited history

import { useUndoRedoState } from "rooks";
 
export default function DrawingApp() {
  const [shapes, setShapes, controls] = useUndoRedoState([], { maxDepth: 5 });
 
  const addShape = (shape) => {
    setShapes(prevShapes => [...prevShapes, shape]);
  };
 
  const addCircle = () => {
    addShape({
      type: "circle",
      id: Date.now(),
      x: Math.random() * 300,
      y: Math.random() * 200
    });
  };
 
  const addSquare = () => {
    addShape({
      type: "square",
      id: Date.now(),
      x: Math.random() * 300,
      y: Math.random() * 200
    });
  };
 
  return (
    <div>
      <div>
        <button onClick={addCircle}>Add Circle</button>
        <button onClick={addSquare}>Add Square</button>
        <button onClick={() => setShapes([])}>Clear All</button>
      </div>
      <div>
        <button onClick={controls.undo} disabled={!controls.isUndoPossible}>
          Undo
        </button>
        <button onClick={controls.redo} disabled={!controls.isRedoPossible}>
          Redo
        </button>
        <span style={{ marginLeft: 20 }}>
          History limited to 5 actions
        </span>
      </div>
      <div style={{ border: "1px solid #ccc", width: 400, height: 300, position: "relative" }}>
        {shapes.map(shape => (
          <div
            key={shape.id}
            style={{
              position: "absolute",
              left: shape.x,
              top: shape.y,
              width: 20,
              height: 20,
              backgroundColor: shape.type === "circle" ? "red" : "blue",
              borderRadius: shape.type === "circle" ? "50%" : "0"
            }}
          />
        ))}
      </div>
      <p>Shapes count: {shapes.length}</p>
    </div>
  );
}

Arguments

Argument valueTypeDescriptionDefault
initialStateTThe initial state value-
optionsObjectConfiguration options (optional)

Options

Options valueTypeDescriptionDefault
maxDepthNumberMaximum number of states to keep in history. Older states are automatically removed100

Returns

Returns an array with three elements:

Return valueTypeDescriptionDefault
stateTThe current state valueinitialState
setStateFunctionFunction to update the state (supports both values and functions)-
controlsObjectAn object containing undo/redo methods and state flags

Control Methods

MethodTypeDescription
undoFunctionMove to the previous state in history
redoFunctionMove to the next state in history
canUndoFunctionDeprecated: Use isUndoPossible instead. Returns boolean
canRedoFunctionDeprecated: Use isRedoPossible instead. Returns boolean
clearUndoStackFunctionClear all undo history
clearRedoStackFunctionClear all redo history
clearAllFunctionClear both undo and redo history

Control Properties

PropertyTypeDescription
isUndoPossibleBooleanWhether an undo operation is possible
isRedoPossibleBooleanWhether a redo operation is possible

Notes

  • The setState function accepts both direct values and function updaters, just like React's useState
  • When a new state is set, the redo history is automatically cleared
  • The hook maintains separate undo and redo stacks internally
  • History is limited by the maxDepth option to prevent memory issues
  • The canUndo and canRedo methods are deprecated in favor of the isUndoPossible and isRedoPossible properties

On this page