Building Optimistic UI in React with Anchor
Learn how to build responsive user interfaces with optimistic updates using Anchor's reactive state management and undoable utility.
What You'll Learn
In this tutorial, you'll learn:
- How to implement optimistic UI patterns with Anchor
- How to use the
undoableutility for immediate rollback capabilities
Understanding Optimistic UI
Optimistic UI is a pattern where the interface immediately reflects the expected result of a user action before the actual operation completes. This approach makes applications feel faster and more responsive to users.
With Anchor's undoable utility, we can implement optimistic UI patterns that allow us to immediately show the result of an action and provide an easy way to rollback if something goes wrong.
Basic Optimistic UI Example
Here's a simple example using setTimeout to simulate an API call and optimistic UI updates:
import '@tailwindcss/browser';
import { undoable } from '@anchorlib/core';
import { observer, useAnchor } from '@anchorlib/react';
import { LoaderCircle } from 'lucide-react';
const Counter = observer(() => {
const [state] = useAnchor({ count: 0, loading: false });
const incrementAsync = () => {
// Prevent multiple clicks.
if (state.loading) return;
// Mark the operation as in progress.
state.loading = true;
// Optimistically update the UI.
const [undo, clear] = undoable(() => {
state.count += 1;
});
// Simulate API call with setTimeout.
setTimeout(() => {
// Simulate random failure 30% of the time.
if (Math.random() < 0.3) {
// Rollback the change if "API call" fails.
undo();
console.log('Operation failed! Rolled back.');
} else {
// Mark the operation as success/complete.
clear();
console.log('Operation succeeded!');
}
// Allow the user to click the button again.
state.loading = false;
}, 1000);
};
return (
<div className="w-screen h-screen flex flex-col items-center justify-center gap-6">
<h2 className="flex items-center gap-4">
Counter: {state.count} {state.loading ? <LoaderCircle size={16} className="animate-spin" /> : null}
</h2>
<button className="px-4 py-2 bg-blue-500 text-white rounded-md" onClick={incrementAsync}>
Increment Async (Optimistic)
</button>
</div>
);
});
export default Counter;Try it Yourself
In This Example
- When the user clicks the button, we immediately increment the counter and show a loading indicator.
- We use
undoableto capture this change, allowing us to rollback if needed. - We simulate an
API callusingsetTimeout. - If the
API callfails (simulated 30% of the time), we rollback the change using theundofunction. - If the
API callsucceeds, we mark the operation as complete andclearthe tracked changes to prevent memory leaks. - We prevent multiple clicks on the button to prevent race conditions.
How undoable Works
The undoable utility captures state changes that occur during an operation and provides a way to rollback those changes:
import { undoable } from '@anchor/core';
// Perform an operation that can be undone
const [undo, clear] = undoable(() => {
// Any state changes here are captured
state.value = newValue;
});
// To rollback the changes
undo();
// To cleanup the changes
clear();Key Points for Optimistic UI
- Optimistic UI is for async operations: It only makes sense when there's a delay between user action and actual result
- Use
undoableto capture changes: This allows you to rollback if the operation fails - Rollback on failure: Call the undo function when your async operation fails.
- Cleanup on succeeds: Call the clear function when your async operation succeeds to prevent memory leaks.