React useOptimistic — Optimistic UI Updates
Advertisement
React useOptimistic — Optimistic UI Updates
Optimistic updates create instant user feedback while operations complete in the background.
- React useOptimistic — Optimistic UI Updates
- Basic Usage
- With useFormStatus
- Real-World: Shopping Cart
- FAQ
Basic Usage
'use client'
import { useOptimistic } from 'react'
export function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React' }
])
const [optimisticTodos, addOptimisticTodo] = useOptimistic(todos)
async function handleAddTodo(formData) {
const newTodo = { id: Date.now(), text: formData.get('todo') }
// Update UI immediately
addOptimisticTodo(newTodo)
// Server operation
const result = await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo)
})
if (!result.ok) {
setTodos(todos) // Revert on error
}
}
return (
<form action={handleAddTodo}>
{optimisticTodos.map(todo => (
<div key={todo.id}>{todo.text}</div>
))}
<input name="todo" />
<button type="submit">Add</button>
</form>
)
}
With useFormStatus
'use client'
import { useOptimistic } from 'react'
import { useFormStatus } from 'react-dom'
import { addComment } from '@/app/actions'
function CommentForm({ postId, onAddComment }) {
const { pending } = useFormStatus()
return (
<form action={onAddComment}>
<textarea name="text" disabled={pending} />
<button disabled={pending}>
{pending ? 'Posting...' : 'Post'}
</button>
</form>
)
}
export function CommentSection({ postId, initialComments }) {
const [optimisticComments, addOptimisticComment] = useOptimistic(initialComments)
async function handleAddComment(formData) {
const newComment = {
id: Math.random(),
text: formData.get('text')
}
addOptimisticComment(newComment)
const result = await addComment(postId, formData.get('text'))
if (!result.success) {
// Revert optimistic update
}
}
return (
<div>
{optimisticComments.map(comment => (
<div key={comment.id}>{comment.text}</div>
))}
<CommentForm postId={postId} onAddComment={handleAddComment} />
</div>
)
}
Real-World: Shopping Cart
'use client'
import { useOptimistic } from 'react'
import { updateCart } from '@/app/actions'
export function CartItem({ item }) {
const [optimisticQuantity, updateOptimisticQuantity] = useOptimistic(item.quantity)
async function handleQuantityChange(newQuantity) {
updateOptimisticQuantity(newQuantity)
const result = await updateCart(item.id, newQuantity)
if (!result.success) {
updateOptimisticQuantity(item.quantity) // Revert
}
}
return (
<div className="flex items-center gap-4">
<span>{item.name}</span>
<input
type="number"
value={optimisticQuantity}
onChange={(e) => handleQuantityChange(Number(e.target.value))}
/>
<span>${item.price * optimisticQuantity}</span>
</div>
)
}
FAQ
Q: What happens if the server operation fails? A: You must revert the optimistic update manually.
Q: Should I use optimistic updates everywhere? A: Only for operations where instant feedback matters (likes, quantities).
Q: How do I handle errors? A: Check the response and revert if needed using the setter.
Optimistic updates dramatically improve perceived performance and user experience.
Advertisement