React Eventually Consistent State Management
This challenge focuses on building a React component that manages state which is updated asynchronously and might temporarily diverge from the source of truth. This is a common pattern in applications dealing with network requests or distributed systems where immediate consistency isn't always feasible or desirable. The goal is to create a user experience that feels responsive while gracefully handling the eventual synchronization of data.
Problem Description
You need to build a React component that displays a list of items and allows the user to toggle a "completed" status for each item. The state update for toggling the status should be simulated as an asynchronous operation (e.g., a network request).
Key Requirements:
- Display a list of items: Each item should show its name and its current completion status (e.g., "Pending" or "Completed").
- Asynchronous status toggle: When a user clicks on an item to toggle its completion status, an asynchronous operation should be initiated.
- Optimistic UI update: The UI should immediately reflect the intended new state (e.g., if toggled from "Pending" to "Completed", the item should appear completed instantly).
- Handling eventual consistency:
- If the asynchronous operation succeeds, the UI remains in the optimistically updated state.
- If the asynchronous operation fails, the UI should revert to its previous consistent state.
- During the asynchronous operation, a visual indicator (e.g., a spinner or disabled state) should be shown for the affected item.
- Error display: If the asynchronous operation fails, provide a clear visual indication to the user that the operation failed and potentially a reason for the failure.
Expected Behavior:
- User sees a list of items.
- User clicks an item to mark it as complete.
- Immediately, the item visually changes to "Completed," and a loading indicator appears for that item.
- After a simulated delay (representing network latency), if the operation was successful, the loading indicator disappears, and the item remains "Completed."
- After a simulated delay, if the operation failed, the item visually reverts to its "Pending" state, the loading indicator disappears, and an error message is displayed for that item.
Edge Cases:
- Multiple items being toggled concurrently.
- Network errors occurring during the operation.
Examples
Example 1:
Initial State (UI):
Items:
- Task A (Pending)
- Task B (Pending)
User Action: Clicks on "Task A".
Intermediate State (UI - Optimistic Update + Loading):
Items:
- Task A (Completed) [Loading Indicator]
- Task B (Pending)
Simulated Asynchronous Operation Outcome: Success after 1 second.
Final State (UI):
Items:
- Task A (Completed)
- Task B (Pending)
Explanation: The UI immediately updated to reflect Task A as completed. The simulated network request succeeded, so the state remains updated.
Example 2:
Initial State (UI):
Items:
- Task A (Pending)
- Task B (Pending)
User Action: Clicks on "Task A".
Intermediate State (UI - Optimistic Update + Loading):
Items:
- Task A (Completed) [Loading Indicator]
- Task B (Pending)
Simulated Asynchronous Operation Outcome: Failure (e.g., server error) after 1 second.
Final State (UI):
Items:
- Task A (Pending) [Error Message: "Failed to complete task."]
- Task B (Pending)
Explanation: The UI optimistically updated Task A. However, the simulated network request failed. The UI then reverted Task A to its original "Pending" state and displayed an error message.
Example 3: (Concurrent Operations)
Initial State (UI):
Items:
- Task A (Pending)
- Task B (Pending)
- Task C (Pending)
User Actions:
- Clicks on "Task A".
- Immediately clicks on "Task C".
Intermediate State (UI - Optimistic Updates + Loading):
Items:
- Task A (Completed) [Loading Indicator]
- Task B (Pending)
- Task C (Completed) [Loading Indicator]
Simulated Asynchronous Operation Outcome:
- Task A: Success after 1 second.
- Task C: Failure after 0.5 seconds.
Final State (UI):
Items:
- Task A (Completed)
- Task B (Pending)
- Task C (Pending) [Error Message: "Could not update task."]
Explanation: Both Task A and Task C were optimistically updated. Task A's update succeeded. Task C's update failed, causing it to revert and show an error. Task B was unaffected.
Constraints
- The list of items will contain between 1 and 20 items.
- Each item's
idwill be a unique string. - Each item's
namewill be a string. - The
completedstatus will be a boolean. - The asynchronous operation simulating the network request should have a random delay between 500ms and 1500ms.
- The asynchronous operation should have a 20% chance of failing.
- The component should be built using functional React components and TypeScript.
Notes
- Consider using React's
useStateanduseEffecthooks. - For managing loading and error states per item, you might consider a structure that holds this alongside the item data.
- Think about how to prevent the user from interacting with an item that is currently in the process of being updated.
- The asynchronous operation can be simulated using
setTimeoutandPromise.rejectfor failure. - Success looks like a responsive UI that handles temporary inconsistencies gracefully, providing clear feedback to the user about the status of their actions.