概述
useOptimistic 是 React 19 引入的新 Hook,用于实现乐观更新(Optimistic Updates)。它允许你在等待异步操作完成时立即更新 UI,提供更好的用户体验。
基本语法
const [optimisticState, addOptimistic] = useOptimistic<State, Patch>(
state,
updateFn
);
参数说明
-
state: 当前状态值
- 可以是任何类型的值
- 作为乐观更新的基础状态
-
updateFn: 更新函数
- 类型:
(currentState: State, patch: Patch) => State
- 接收当前状态和更新补丁
- 返回新的乐观状态
- 类型:
返回值
type UseOptimisticReturn<State, Patch> = [
State, // 乐观状态
(patch: Patch) => void // 触发乐观更新的函数
];
使用示例
1. 基础点赞功能
function LikeButton({ id, initialLikes }: { id: string; initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes);
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(currentLikes: number, addedLikes: number) => currentLikes + addedLikes
);
async function handleLike() {
addOptimisticLike(1); // 立即更新 UI
try {
const response = await fetch(`/api/like/${id}`, { method: 'POST' });
const newLikes = await response.json();
setLikes(newLikes); // 更新实际状态
} catch (error) {
// 错误处理
setLikes(likes); // 回滚到原始状态
}
}
return (
<button onClick={handleLike}>
Likes: {optimisticLikes}
</button>
);
}
2. 评论列表
interface Comment {
id: string;
text: string;
author: string;
}
function CommentList() {
const [comments, setComments] = useState<Comment[]>([]);
const [optimisticComments, addOptimisticComment] = useOptimistic<
Comment[],
Comment
>(
comments,
(currentComments, newComment) => [...currentComments, newComment]
);
async function handleAddComment(text: string) {
const optimisticComment = {
id: 'temp-' + Date.now(),
text,
author: 'Current User',
};
addOptimisticComment(optimisticComment);
try {
const response = await fetch('/api/comments', {
method: 'POST',
body: JSON.stringify({ text }),
});
const savedComment = await response.json();
setComments([...comments, savedComment]);
} catch (error) {
// 错误处理,回滚
setComments(comments);
}
}
return (
<div>
<CommentForm onSubmit={handleAddComment} />
<ul>
{optimisticComments.map(comment => (
<CommentItem key={comment.id} comment={comment} />
))}
</ul>
</div>
);
}
3. 复杂状态更新
interface TodoItem {
id: string;
text: string;
completed: boolean;
}
function TodoList() {
const [todos, setTodos] = useState<TodoItem[]>([]);
const [optimisticTodos, addOptimisticUpdate] = useOptimistic<
TodoItem[],
{ id: string; completed: boolean }
>(
todos,
(currentTodos, update) =>
currentTodos.map(todo =>
todo.id === update.id
? { ...todo, completed: update.completed }
: todo
)
);
async function toggleTodo(id: string, completed: boolean) {
addOptimisticUpdate({ id, completed });
try {
await fetch(`/api/todos/${id}`, {
method: 'PATCH',
body: JSON.stringify({ completed }),
});
} catch (error) {
// 回滚
setTodos(todos);
}
}
return (
<ul>
{optimisticTodos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
/>
))}
</ul>
);
}
最佳实践
- 错误处理与回滚
function useOptimisticAction<T, P>(
initialState: T,
updateFn: (state: T, patch: P) => T,
actionFn: (patch: P) => Promise<T>
) {
const [state, setState] = useState<T>(initialState);
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
async function performAction(patch: P) {
addOptimistic(patch);
try {
const result = await actionFn(patch);
setState(result);
} catch (error) {
setState(state); // 回滚
throw error;
}
}
return [optimisticState, performAction] as const;
}
- 与 Suspense 集成
function AsyncList() {
return (
<Suspense fallback={<Skeleton />}>
<OptimisticList />
</Suspense>
);
}
注意事项
-
性能考虑:
- 避免在更新函数中进行复杂计算
- 合理使用 useMemo 缓存计算结果
- 注意大型列表的渲染优化
-
状态一致性:
- 保持乐观更新和实际状态的同步
- 实现合适的回滚机制
- 处理并发更新情况
-
用户体验:
- 提供适当的加载状态指示
- 实现平滑的状态转换
- 处理错误情况下的用户反馈
总结
-
useOptimistic 优点:
- 提升用户体验
- 减少感知延迟
- 支持复杂状态更新
- 易于实现回滚
-
适用场景:
- 点赞/收藏功能
- 评论系统
- 待办事项列表
- 表单提交
- 数据列表操作
-
使用建议:
- 合理处理错误情况
- 实现优雅的回滚机制
- 注意状态一致性
- 优化性能表现