前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库React flushSync 同步刷新 DOM 使用代价
ReReact状态与渲染

React flushSync 什么时候有必要,为什么它不会让旧闭包变成新状态?

flushSync 用于少量确实需要同步 DOM 结果的集成场景,调用返回后的 DOM 和旧局部变量是两回事。

前端进阶之旅 · 一题精讲更新于 2026.09.06
React#状态与渲染
先看核心答案读代码示例
理解线索

明确同步边界的输入与输出

  1. 提交状态更新回调只安排需要立即生效的有限变更
  2. 同步完成提交React 把相关结果同步到 DOM
  3. 调用外部接口随后执行确实依赖新节点的命令式动作

显示输入框后聚焦只是一个可验证的最小例子;真实界面还可用回调 ref 等方式表达节点出现后的动作。

核心回答

先记住这个答案

flushSync 可以强制 React 同步处理回调中的更新,使回调结束并返回后相关 DOM 已可供后续同步代码使用。它主要用于与浏览器或第三方命令式接口衔接,不应成为普通 setter 的默认包装。为了完成这次同步工作,React 还可能冲刷其他待处理更新、Effect,或让挂起的内容重新显示 fallback。它改变的是提交时机,不会重绑定当前事件处理器捕获的 state;因此同步之后读取旧变量仍可能得到原值。

  • 调用返回后读取的是已经更新的 DOM
  • 闭包快照不会因同步提交而被改写
  • 可能连带处理其他工作并增加交互成本

为什么 setter 下一行的 ref 可能还是空

条件输入框尚未挂载时,普通 setOpen(true) 只请求后续渲染,紧接着访问 inputRef.current 可能还没有节点。若当前集成要求在这个调用栈里拿到节点,可以把有限更新放入 flushSync,再在外部读取 ref。

下面点击编辑后,输入框会在同步边界内出现,后面的 focus 才能操作它。示例把动作写在事件处理器中,不在渲染函数或 Effect 执行期间强行要求 React 再开始一次同步渲染。

同步创建节点后再聚焦TSX
import { useRef, useState } from 'react';
import { flushSync } from 'react-dom';

export default function InlineEditor() {
  const [open, setOpen] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);
  function edit() {
    flushSync(() => setOpen(true));
    inputRef.current?.focus();
  }
  return <>
    <button onClick={edit}>编辑标题</button>
    {open && <input ref={inputRef} aria-label="标题" defaultValue="面试笔记" />}
  </>;
}

点击后可以立即对新挂载的输入框聚焦。如果只是希望节点出现时执行聚焦,可考虑回调 ref;不要为了省去生命周期设计而给每一个普通状态更新加同步包装。

强制提交不会改写事件里的局部变量

即使同步之后页面已经显示输入框,edit 函数中捕获的 open 仍是进入这次处理器时的值。要验证当前 DOM 可以读取 ref 或节点状态;要表达下一状态,可以使用明确的局部 next,而不是期待旧变量被框架原地修改。

同样,flushSync 接收的回调不是可等待整个异步业务的事务。若回调里启动 Promise,后续异步完成的更新不会因为曾位于这个函数定义中,就自动全部获得同一次同步提交保证。应把同步范围保持清晰且短小。

成本可能超过回调里看起来很少的代码

一个小 setter 也可能影响较大的子树,React 为了冲刷它还可能处理回调之外必要的待执行工作。Effect 及其安排的更新可能提前运行,Suspense 边界也可能重新显示加载内容,不能只按回调行数估算成本。

在滚动、输入等高频事件中频繁使用会压缩浏览器处理交互和绘制的空间。先用正常状态与提交后的动作完成需求,只有外部同步契约确实要求时才保留,并通过真实交互检查页面是否出现多余提交、闪烁或明显延迟。

回答前,多想一步

容易答错的地方

flushSync 是修复所有 state 旧值的通用方法
它同步 DOM 提交,不改变 JavaScript 闭包绑定。旧变量问题应从快照和更新函数解释,不能靠强制渲染掩盖数据读取语义。
回调里只有一个 setter,所以没有额外代价
更新可能传播到更大子树,还可能连带冲刷其他待处理工作;应根据实际提交和交互测量,不能用源代码行数判断性能成本。
试着用自己的话回答

面试官还会怎么问?

为什么不直接给新输入框使用 autoFocus?

对于简单的首次出现聚焦,声明式属性或回调 ref 可能足够。flushSync 更适合外部同步流程确实要立即继续读取 DOM 的情况,不应为了示例而扩大使用。

可以在 useEffect 里面直接调用 flushSync 吗?

不应在 React 已经执行生命周期工作的过程中这样要求同步冲刷。需要重新设计触发位置或使用正常更新,避免把同步边界嵌进正在进行的渲染流程。

flushSync 返回是否说明远程保存也成功了?

不说明,它只涉及 React 的本地更新与 DOM 提交。网络动作有独立的完成、失败和副作用状态,需要通过请求结果或业务查询确认。

从一道题,走向一组知识

把知识连起来

状态与渲染

React 事件里的多个 setState 怎样批处理,为什么不能只数 render 日志?

正常更新通常依赖 React 的批处理安排

Hooks 与复用

React 回调 ref 适合什么场景,条件 DOM 出现后为什么不一定需要 Effect?

节点出现时的命令式动作也可以由回调 ref 承担

状态与渲染

React state 为什么是渲染快照,setState 后的回调为什么仍读到旧值?

同步提交仍不会重写已有事件闭包里的状态

参考资料

  • React:flushSync
  • React:Manipulating the DOM with refs

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 为什么 setter 下一行的 ref 可能还是空
  3. 强制提交不会改写事件里的局部变量
  4. 成本可能超过回调里看起来很少的代码
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑