Hone logo
Hone
Problems

React Time Travel Debugging

Implement a "time travel" feature for a React component, allowing users to navigate through past states of the component. This is a fundamental concept in advanced debugging and state management, enabling developers to inspect component behavior at different points in its history.

Problem Description

Your task is to create a React component that records its state history and provides functionality to revert to previous states. This will simulate a "time travel" debugging experience.

What needs to be achieved:

  • A React component that maintains its current state.
  • A mechanism to record each state change as an entry in a history log.
  • UI elements (e.g., buttons) to navigate forward and backward through the recorded history.

Key Requirements:

  1. State Management: The component must have a state that can be updated.
  2. History Logging: Every time the state changes, the previous state should be stored in an array or similar data structure.
  3. Navigation:
    • A "Previous" button should revert the component's state to the previous entry in the history.
    • A "Next" button should advance the component's state to the next entry in the history.
  4. Cursor/Index: Maintain a pointer or index to the "current" state being displayed in the history. When navigating, this pointer should move accordingly.
  5. Disabling Buttons: The "Previous" button should be disabled if there are no previous states to go back to. The "Next" button should be disabled if the current state is the most recent one in the history.
  6. New State Creation: When the user makes a change that creates a new state (not by navigating back), any future states in the history that were branched off from the state before the new change should be discarded. For example, if the history is A -> B -> C and the user navigates back to B and then makes a new change D, the history should become A -> B -> D, not A -> B -> C -> D.

Expected Behavior:

  • The component should initialize with an initial state.
  • When the state is modified by user interaction, a new entry is added to the history, and the current pointer advances to this new state.
  • Clicking "Previous" moves the pointer back one step in the history and updates the component's state.
  • Clicking "Next" moves the pointer forward one step in the history and updates the component's state.
  • The UI should reflect the current state and the available navigation options.

Edge Cases:

  • Initial State: Ensure proper handling when there's only one state in history.
  • Branching History: Correctly manage history when reverting and then making a new change.

Examples

Example 1: Let's consider a simple counter component.

Initial State: count: 0 History: [{ count: 0 }] Current Index: 0

User Action: Clicks an "Increment" button. New State: count: 1 History: [{ count: 0 }, { count: 1 }] Current Index: 1

User Action: Clicks an "Decrement" button. New State: count: 0 History: [{ count: 0 }, { count: 1 }, { count: 0 }] Current Index: 2

UI:

  • Display Count: 0
  • "Previous" button is enabled.
  • "Next" button is disabled.

User Action: Clicks "Previous". Current Index: 1 Component State: count: 1

UI:

  • Display Count: 1
  • "Previous" button is enabled.
  • "Next" button is enabled.

User Action: Clicks "Previous" again. Current Index: 0 Component State: count: 0

UI:

  • Display Count: 0
  • "Previous" button is disabled.
  • "Next" button is enabled.

Example 2: (Branching History Scenario)

History: [stateA, stateB, stateC] Current Index: 2 (showing stateC)

User Action: Clicks "Previous" twice. Current Index: 0 (showing stateA)

User Action: User triggers an action that leads to stateD. New State: stateD History: [stateA, stateD] (Note: stateB and stateC are discarded) Current Index: 1

UI:

  • Displays stateD's properties.
  • "Previous" button is enabled.
  • "Next" button is disabled.

Constraints

  • The state can be any valid JSON serializable JavaScript object.
  • The component should be implemented using React and TypeScript.
  • The solution should aim for clear and readable code.
  • Avoid external state management libraries like Redux or Zustand for the core time travel logic. You can use useState and useReducer.

Notes

Consider how to store the history. An array of state objects is a common approach. Pay close attention to immutability when updating state and history. When storing previous states, ensure you are storing a copy of the state at that moment, not a reference to the mutable state object itself. This is crucial for preventing unintended side effects when navigating through history. Think about how to handle the "branching" aspect of history, especially when a new action occurs after navigating back.

Loading editor...
typescript