Hone logo
Hone
Problems

Implementing a useStateHistory Hook in React

The useStateHistory hook provides a way to track and revert to previous states of a React component's state. This is particularly useful for implementing undo/redo functionality, debugging, or creating more complex state management scenarios where you need to maintain a history of state changes. This challenge asks you to implement this hook from scratch.

Problem Description

You are tasked with creating a custom React hook called useStateHistory. This hook should extend the functionality of the built-in useState hook by maintaining a history of state changes. The hook should accept an initial state value as an argument and return an array containing:

  1. The current state value.
  2. A function to update the state (similar to setState in useState).
  3. A function to undo the last state change.
  4. A function to redo the last undone state change.
  5. The current history index (an integer representing the position of the current state in the history array).

Key Requirements:

  • History Tracking: Every time the state is updated using the provided update function, the previous state should be pushed onto a history array.
  • Undo Functionality: The undo function should revert the state to the previous state in the history, decrementing the history index.
  • Redo Functionality: The redo function should revert the state to the next state in the history (if available), incrementing the history index.
  • History Limit: The history array should have a maximum size. When a new state is added and the history is full, the oldest state should be removed.
  • Immutability: State updates should be handled immutably. The history array should store copies of the state, not references.
  • Edge Cases:
    • Calling undo when the history is empty should have no effect.
    • Calling redo when the history is exhausted (at the end) should have no effect.
    • The history index should not go below 0 or exceed the history length.

Expected Behavior:

The hook should behave similarly to useState but with the added functionality of undo/redo. The component using the hook should re-render whenever the state changes, is undone, or is redone.

Examples

Example 1:

Input:
Initial state: "Hello"
Update function called with "World":
Output:
Current state: "World"
History: ["Hello", "World"]
History index: 1

Undo function called:
Output:
Current state: "Hello"
History: ["Hello", "World"]
History index: 0

Redo function called:
Output:
Current state: "World"
History: ["Hello", "World"]
History index: 1

Example 2:

Input:
Initial state: 0
History limit: 3
Update function called with 1:
Update function called with 2:
Update function called with 3:
Update function called with 4:
Output:
Current state: 4
History: [0, 1, 2]
History index: 2

Example 3: (Edge Case - Undo beyond the beginning)

Input:
Initial state: "A"
History limit: 2
Update function called with "B":
Undo function called:
Undo function called:
Output:
Current state: "A"
History: ["A", "B"]
History index: 0

Constraints

  • History Limit: The history limit should be configurable and defaults to 5.
  • Input Type: The initial state can be of any type.
  • Performance: The hook should be performant, avoiding unnecessary re-renders or complex operations. Consider the cost of copying state objects.
  • Immutability: Ensure that state updates are handled immutably to prevent unexpected side effects.

Notes

  • Consider using the spread operator (...) or Object.assign() to create copies of state objects when updating the history.
  • Think about how to efficiently manage the history array to avoid excessive memory usage.
  • The undo and redo functions should not directly modify the history array; they should update the current state and history index.
  • The history index should be carefully managed to prevent out-of-bounds errors.
  • This is a good opportunity to practice using useState, useRef, and functional updates in React.
Loading editor...
typescript