Optimistic UI

Optimistic UI 是在等待后端响应前先在 UI 上展示预期结果的模式。在 AI 对话中,用户发送消息后立即显示在界面上,不等后端确认。

#type / concept #status / evergreen #tech / frontend #tech / ai

[!info] related notes

Optimistic UI

一句话定义

Optimistic UI 是在等待后端响应前先在 UI 上展示预期结果的模式。在 AI 对话中,用户发送消息后立即显示在界面上,不需要等后端返回确认。

它解决什么问题

如果用户发送消息后要等后端确认才显示:

  1. 网络延迟导致 UI 卡顿
  2. 用户不确定消息是否发送成功
  3. 快速连续发送时体验差

Optimistic UI 让界面立即响应,后端异步确认。

核心原理

传统流程:
  用户输入 → 发送请求 → 等待响应 → 显示消息
  (延迟 200-500ms)

Optimistic 流程:
  用户输入 → 立即显示消息 → 发送请求 → 后台确认/回滚
  (延迟 0ms)

实现

function useChat() {
  const [messages, setMessages] = useState([]);

  const sendMessage = async (content: string) => {
    // 1. 立即添加用户消息(乐观更新)
    const tempId = `temp_${Date.now()}`;
    setMessages(prev => [...prev, {
      id: tempId,
      role: 'user',
      content,
      status: 'sending', // 临时状态
    }]);

    // 2. 创建 assistant 消息占位
    setMessages(prev => [...prev, {
      id: `assistant_${tempId}`,
      role: 'assistant',
      content: '',
      status: 'streaming',
    }]);

    // 3. 发送请求
    try {
      const response = await fetch('/api/chat', { ... });
      // 成功:更新为正式 ID
      setMessages(prev => prev.map(msg =>
        msg.id === tempId
          ? { ...msg, id: response.messageId, status: 'sent' }
          : msg
      ));
    } catch (err) {
      // 失败:标记为错误
      setMessages(prev => prev.map(msg =>
        msg.id === tempId
          ? { ...msg, status: 'error' }
          : msg
      ));
    }
  };

  return { messages, sendMessage };
}

常见设计模式

1. 临时 ID

乐观更新使用临时 ID,后端确认后替换为正式 ID。

2. 状态标记

sendingsent / error

3. 回滚机制

请求失败时回滚 UI 状态,显示错误提示。

常见坑

  1. 不做回滚: 请求失败后 UI 仍然显示已发送
  2. ID 不替换: 临时 ID 没有替换为正式 ID,后续操作引用错误
  3. 不做重试: 发送失败后没有重试机制
  4. 状态不一致: 多个乐观更新并发时状态冲突

参考资料

创建于 2026/6/30 更新于 2026/7/15