Hone logo
Hone
Problems

Implement useStateHistory Hook in React

Create a custom React hook, useStateHistory, that tracks the history of a state variable. This hook should allow you to access the current state, a function to update the state, and an array representing the history of all previous states. This is useful for implementing features like undo/redo functionality or for debugging state changes.

Problem Description

You need to implement a custom React hook named useStateHistory that mimics the behavior of useState but also maintains a history of the state's values.

Requirements:

  • State Management: The hook should accept an initial state value and return the current state and a function to update it, similar to React.useState.
  • History Tracking: Every time the state is updated, the previous state should be added to a history array.
  • Access to History: The hook should return the history array, containing all previously held states in chronological order (oldest first, most recent last).
  • Initial State: The initial state should be included in the history.

Expected Behavior:

When the state is updated using the returned setter function, the hook should:

  1. Store the current state in the history array.
  2. Update the current state to the new value.

Edge Cases:

  • No Updates: If the state is never updated, the history should contain only the initial state.
  • Multiple Updates: The history array should correctly store all intermediate states.

Examples

Example 1:

import { renderHook, act } from '@testing-library/react-hooks';
import { useStateHistory } from './useStateHistory'; // Assume this is your hook file

function App() {
  const [count, setCount, history] = useStateHistory(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
      <p>History: {JSON.stringify(history)}</p>
    </div>
  );
}

// Test Scenario
const { result } = renderHook(() => useStateHistory(0));
const [count, setCount, history] = result.current;

console.log(count); // 0
console.log(history); // [0]

act(() => {
  setCount(1);
});

console.log(result.current[0]); // 1
console.log(result.current[2]); // [0, 1]

act(() => {
  setCount(2);
});

console.log(result.current[0]); // 2
console.log(result.current[2]); // [0, 1, 2]

Input: Initial state 0. Output:

  • Current state: 0
  • History: [0]

Explanation: Upon initial render, the state is 0, and the history contains only the initial state.

Example 2:

Following the steps in Example 1: After setCount(1) is called:

  • Current state: 1
  • History: [0, 1]

Explanation: The previous state 0 was added to the history, and the current state is updated to 1.

Example 3:

Following the steps in Example 2: After setCount(2) is called:

  • Current state: 2
  • History: [0, 1, 2]

Explanation: The previous state 1 was added to the history, and the current state is updated to 2.

Constraints

  • The hook should be implemented using standard React hooks (useState, useEffect, etc.).
  • The history array should store all states, including the initial state.
  • The hook should be generic, accepting any type for the state.

Notes

  • Consider how React.useState handles its setter function (it can accept a new value or a function that receives the previous state). Your setCount should ideally support this.
  • The history should be ordered chronologically. The first element in the history array should be the oldest state, and the last element should be the most recent state before the current one.
  • You might want to consider immutability if dealing with complex objects or arrays as state. For this challenge, shallow copies are acceptable if needed for history tracking.
Loading editor...
typescript