React useOptimistic 是什么?乐观更新失败后,界面怎样恢复?
下面是一段教学模拟,不是真实面试记录。
🧑💻 面试官:用户点击点赞,为什么页面可以马上加一?
🙋♂️ 我:先改状态,服务器失败再减一。
🧑💻 面试官:用户连续点两次,两个请求一个成功、一个失败,减一还准确吗?
🙋♂️ 我:要区分每个请求。
🧑💻 面试官:那 useOptimistic 保存的是服务端已经确认的状态,还是等待结果时的临时展示?
乐观更新提前改变的是「展示」,不是事实。要把服务器确认的基础状态,与等待期间的临时状态分开管理。
面试速答(60 秒版)
useOptimistic 用于在异步 Action 等待期间,根据基础状态生成临时的乐观状态,让用户不必一直等服务器确认。
例如发布评论时,可以先显示一条“发送中”的评论。成功后,把服务端确认的评论写入基础状态;失败后,临时展示结束,并向用户说明失败或提供重试。
它不是数据库回滚工具,也不替我们保证请求成功。并发提交时,还需要临时 ID、请求关联和明确的合并规则,不能只在失败时机械地减一。
因此,适合提前展示、且失败可以清楚恢复的操作可以使用;高风险动作仍要以服务端结果为准,不能把临时显示当成完成凭证。

知识点详解:界面可以先走一步,事实不能省掉确认
基础状态与临时状态,分别存什么?
假设页面已经有两条服务端确认的评论。用户提交第三条时,我们希望立即看到内容。
基础状态仍是原来的两条。乐观状态则根据基础状态,加上一条带临时 ID 和“发送中”标记的新评论。
这样就能区分“服务器确实保存了什么”和“为了及时反馈,界面先展示什么”。useOptimistic 的更新函数负责根据基础状态与乐观输入计算展示结果,应保持纯计算,而不是在里面发送请求。官方 Hook 说明
如果把临时评论直接永久写成已成功数据,又没有后续确认,乐观更新就变成了假成功。
成功时,要接住服务端确认后的结果
服务器可能调整内容、产生正式 ID 或返回新的排序位置。请求成功后,应把这份确认结果更新到基础状态。
接下来临时展示结束,页面仍有正式评论,而不是第三条突然消失。
需要明确临时 ID 和正式 ID 的对应关系,避免同一条评论同时显示两遍。不要只在网络成功时把状态标签改成“已发送”,却继续保存一个永远没有正式身份的临时对象。
Hook 与 Action 提供的是状态协作机制,服务端结果怎样进入应用状态仍需要我们完成。
失败后恢复,不等于只做一次反向操作
假设两个提交 A、B 并发进行,A 成功后刷新了列表,B 随后失败。
如果使用“出错就把列表长度减一”,可能删掉已经确认的 A,而不是失败的 B。
因此每次临时变化需要可识别身份。失败后应结束对应 Action 的临时影响,再结合真实基础状态展示。我们还要保存用户的错误反馈或可重试内容,不能只让条目凭空消失。
另外,超时不一定表示服务器没处理。如果用户重试,服务端应根据操作语义考虑幂等。界面恢复并不能证明真实副作用已经撤销。

哪些操作值得乐观展示?
普通点赞、低风险偏好设置或评论草稿,可以评估这种方式。用户能理解等待状态,失败也有清楚处理路径。
付款、权限授予和不可逆删除,不应该仅凭乐观界面宣称成功。可以先显示“正在处理”,但最终凭证必须来自服务端确认。
React 没有同一套 Python Hook 实现。Python 服务可以接收这些请求并提供一致的结果、幂等和权限检查;不能把后端事务称为 useOptimistic 的 Python 版本。
面试官继续追问
useOptimistic 会替我自动取消失败的网络请求吗?
不会。它处理展示状态,不是请求执行器。取消、超时和重试仍由具体请求层处理。
服务器成功,但返回响应丢了,界面恢复成失败怎么办?
需要区分“确定拒绝”和“结果未知”。可以查询操作状态或用幂等标识重试,不能直接认为没落库,再重复执行不可逆动作。
基础状态在等待期间改变,乐观状态怎么办?
它需要基于新的基础状态重新计算。因此更新函数应描述“增加这条待发评论”等操作,而不是拿一份早已过时的整张列表强行覆盖。
面试速记卡
- 基础状态:保存已经确认的结果。
- 乐观状态:在 Action 等待期间提供临时展示。
- 成功处理:将服务端确认结果更新进基础状态。
- 失败处理:恢复对应临时影响,并保留错误和重试反馈。
- 关键边界:恢复界面不等于撤销服务端副作用。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →