跳到主要内容

乐观更新

useOptimistic 如何提前展示操作结果?​

  • 临时覆盖: useOptimistic(value, reducer?) 返回乐观值与更新函数,Action 待完成期间展示预测结果,没有待完成 Action 时回到传入的真实 value;
  • 调用范围: 乐观 setter 必须在 Action 中调用,不能在渲染中更新,也不能在范围外调用后期待持续的 pending 显示;
  • 真实来源: 服务端成功后仍须更新父级 props 或 state,乐观值本身不会永久替代真实数据;

如何为一次异步操作加入乐观反馈?​

import { startTransition, useOptimistic, useState } from "react";

function LikeButton({ saveLike }) {
const [liked, setLiked] = useState(false);
const [optimisticLiked, setOptimisticLiked] = useOptimistic(liked);
const [error, setError] = useState("");
function like() {
startTransition(async () => {
setOptimisticLiked(true);
setError("");
try {
await saveLike();
startTransition(() => setLiked(true));
} catch {
setError("未能点赞,请重试");
}
});
}
return (
<>
<button disabled={optimisticLiked} onClick={like}>
{optimisticLiked ? "已点赞" : "点赞"}
</button>
{error && <p role="alert">{error}</p>}
</>
);
}

多个乐观操作如何叠加在最新真实数据上?​

  • Reducer: 第二参数以最新基础状态和 action 计算临时结果,必须纯粹;基础 value 变化时 React 会重新应用乐观计算;
  • 避免旧闭包: 列表追加用 reducer 的 currentState,而不是从创建回调时的旧 items 拼接;
  • 多种动作: 通过 action.type 表达增加、删除等操作,每个乐观条目可带临时 ID 与 pending 标记,服务端确认后由真实结果替换;
const [visibleMessages, addOptimistic] = useOptimistic(messages, (current, newMessage) => [
...current,
{ ...newMessage, pending: true },
]);

async function sendAction(text) {
const draft = { id: crypto.randomUUID(), text };
addOptimistic(draft);
await sendMessage(text);
// 在此等待上层真实 messages 更新完成。
}
  • 调用约定: sendAction 应由所属 Action 路径调用,sendMessage 的集成要更新真实列表,否则 Action 结束后临时消息会消失;

如何处理失败与显示待处理状态?​

  • 回退: Action 结束后显示真实 value,若失败时未改变真实值,临时覆盖自然消失;同时给用户明确失败反馈;
  • 不自动撤销: 回退只是 UI 恢复,不能取消已成功写入的远端操作;
  • 待处理来源: useOptimistic 不直接返回 pending,可使用所属 Transition、useActionState 或条目状态表达;