React 19 useOptimistic: UX mượt hơn với optimistic updates
Tìm hiểu useOptimistic hook mới trong React 19 — cách implement optimistic UI đúng cách, kèm ví dụ like button và comment form.
Một trong những bổ sung hay nhất của React 19 là useOptimistic — một hook chuyên dụng cho optimistic updates. Trước đây bạn phải tự quản lý pending state, rollback logic. Giờ React lo hết.
Optimistic Update là gì?
Khi user click "Like", thay vì đợi server response rồi mới update UI, bạn update ngay lập tức và giả sử request sẽ thành công. Nếu fail, rollback. Kết quả: UI phản hồi instant, user cảm giác app rất nhanh.
useOptimistic API
Hook nhận vào state hiện tại và một reducer function. Trả về optimistic state và một dispatch function:
"use client";
import { useOptimistic, useTransition } from "react";
import { toggleLike } from "@/actions/blog-post.actions";
interface Props {
postId: string;
initialLiked: boolean;
initialCount: number;
}
export function LikeButton({ postId, initialLiked, initialCount }: Props) {
const [isPending, startTransition] = useTransition();
const [optimistic, setOptimistic] = useOptimistic(
{ liked: initialLiked, count: initialCount },
(current, newLiked: boolean) => ({
liked: newLiked,
count: newLiked ? current.count + 1 : current.count - 1,
})
);
const handleClick = () => {
startTransition(async () => {
setOptimistic(!optimistic.liked);
await toggleLike(postId);
});
};
return (
<button
onClick={handleClick}
disabled={isPending}
className={optimistic.liked ? "text-red-500" : "text-gray-400"}
>
{optimistic.liked ? "❤️" : "🤍"} {optimistic.count}
</button>
);
}
Cách nó hoạt động
1. User click → setOptimistic update UI ngay lập tức với giá trị mới.
2. Server Action toggleLike chạy async trên server.
3a. Nếu thành công → React revalidate data, optimistic state merge với real state.
3b. Nếu thất bại → React tự động rollback về state trước đó. Không cần try/catch thủ công.
Ví dụ 2: Comment form
Optimistic updates đặc biệt hiệu quả với lists — thêm comment mới ngay khi submit:
"use client";
import { useOptimistic } from "react";
import { addComment } from "@/actions/blog-post.actions";
export function CommentSection({ postId, initialComments, currentUser }) {
const [optimisticComments, addOptimisticComment] = useOptimistic(
initialComments,
(state, newComment: { text: string }) => [
...state,
{
id: crypto.randomUUID(),
text: newComment.text,
author: currentUser,
createdAt: new Date().toISOString(),
pending: true, // visual indicator
},
]
);
async function handleSubmit(formData: FormData) {
const text = formData.get("text") as string;
addOptimisticComment({ text });
await addComment(postId, text);
}
return (
<div>
{optimisticComments.map((c) => (
<div key={c.id} className={c.pending ? "opacity-60" : ""}>
<strong>{c.author.name}</strong>: {c.text}
</div>
))}
<form action={handleSubmit}>
<input name="text" placeholder="Viết comment..." />
<button type="submit">Gửi</button>
</form>
</div>
);
}
Tips thực tế
Dùng với useTransition — Wrap server action trong startTransition để có isPending state, hữu ích cho disable button hoặc show spinner.
Thêm visual indicator — Dùng flag pending: true để style khác cho optimistic items (opacity thấp, shimmer effect).
Không cần try/catch — useOptimistic tự rollback khi transition fail. Nhưng bạn vẫn nên handle error để show toast notification.
Kết hợp với Server Actions — useOptimistic + Server Actions là combo hoàn hảo. Form hoạt động progressive enhancement, optimistic UI cho JS-enabled browsers.
Kết luận
useOptimistic giải quyết một vấn đề UX mà trước đây cần nhiều boilerplate. Với React 19, optimistic updates trở thành first-class pattern. Hãy bắt đầu dùng cho mọi mutation — like, comment, bookmark, follow. User sẽ cảm ơn bạn.
Admin
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!