← All tasksReact · Senior

Undo and redo

Write a useHistory(initial) hook - state with a change history. It returns { value, set, undo, redo, canUndo, canRedo }.

set(v) makes v the current value and remembers the previous one. undo() brings back the previous value, redo() the undone one. A new set after undo discards the redo branch. undo and redo do nothing without history. Several set calls in a row in one handler must all be recorded.

Tests (6)

  1. const { result } = renderHook(() => useHistory('a'));
    expect(result.current.value).toBe('a');
    expect(result.current.canUndo).toBe(false);
    expect(result.current.canRedo).toBe(false);
  2. const { result } = renderHook(() => useHistory(0));
    act(() => result.current.set(1));
    act(() => result.current.set(2));
    act(() => result.current.undo());
    expect(result.current.value).toBe(1);
    act(() => result.current.undo());
    expect(result.current.value).toBe(0);
    expect(result.current.canUndo).toBe(false);
  3. const { result } = renderHook(() => useHistory(0));
    act(() => result.current.set(1));
    act(() => result.current.undo());
    expect(result.current.canRedo).toBe(true);
    act(() => result.current.redo());
    expect(result.current.value).toBe(1);
    expect(result.current.canRedo).toBe(false);
  4. const { result } = renderHook(() => useHistory(0));
    act(() => result.current.set(1));
    act(() => result.current.set(2));
    act(() => result.current.undo());
    act(() => result.current.set(3));
    expect(result.current.value).toBe(3);
    expect(result.current.canRedo).toBe(false);
    act(() => result.current.undo());
    expect(result.current.value).toBe(1);
  5. const { result } = renderHook(() => useHistory('x'));
    act(() => result.current.undo());
    act(() => result.current.redo());
    expect(result.current.value).toBe('x');
  6. const { result } = renderHook(() => useHistory(0));
    act(() => {
      result.current.set(1);
      result.current.set(2);
    });
    act(() => result.current.undo());
    expect(result.current.value).toBe(1);