渲染、提交与更新
渲染、提交与更新 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
「渲染」这个词被用得太随意,导致很多判断出错:setState 后界面立即变了吗?父组件更新时子组件为什么也动了?这一课把流程拆成触发、执行渲染、提交三个阶段,读完你能准确说出一次更新里发生了什么,并用手头的工具验证。
问题场景
学习页面上有父组件 StudyPage 和子组件 CourseCard,测试提了个疑问:「CourseCard 的 props 一个字没变,为什么 DevTools 高亮显示它也重新渲染了?是不是浪费?」要回答这个问题,得先弄清「渲染」到底指什么。
核心解释
一次更新分三个阶段,名字先对齐:
- 触发:两种来源——组件首次挂载,或某个组件调用了 setState(在 React 19 里叫更新请求);
- 执行渲染:React 调用受影响的组件函数,算出新的 JSX 树。注意:渲染就是「重新执行函数」,与屏幕显示是两回事;
- 提交:React 把差异应用到真实 DOM(react-dom 负责这一步),然后浏览器重绘。界面变化只发生在提交之后。
把三件事分开后,场景里的疑问就有答案了:默认情况下,一个组件更新时,它的全部子组件函数都会被重新执行——哪怕传给子组件的 props 完全没变。因为 React 不知道子组件依赖了什么,保守地重新计算一遍。重新执行函数本身很便宜(算出一棵「没变化的树」,diff 后发现无需改 DOM,提交阶段什么都不做),所以大多数场景不用管;真正昂贵的是反复操作 DOM,而 React 的 diff 恰好把这一层省掉了。
验证判断靠两个工具:在组件函数体里放 console.log("CourseCard 渲染"),父组件一更新,观察它是否重新打印;用 React DevTools 开启 Highlight updates,谁重新渲染一目了然。
function CourseCard({ title }) {
console.log("CourseCard 渲染"); // 父组件每次更新都会打印,props 没变也一样
return <h3>{title}</h3>;
}
如果某个子组件确实昂贵且 props 不常变,可以用 memo() 让它跳过 props 未变的渲染——但先测量、再优化,这是 R29 之后的话题,本课先把模型建立对。
示例拆解
实验区用一个「渲染计数器」把不可见的过程可视化,关键点如下:
- 父组件
StudyPage有一个countstate 和「+1」按钮;两颗子组件都显示「自己被执行了几次」,计数存在各自useRef里(ref 不触发渲染,只用来计数,细节在 R27); - 子组件 A 接收
count作为 props 并显示;子组件 B 什么都不接收——点一次 +1,两个计数都 +1,证明默认行为是「父更新带动全部子组件函数重新执行」; - 另有一颗子组件持有自己的 state 和按钮,点它只有自己计数变化,父组件不动——子组件更新不会向上传播,数据流方向决定了渲染范围;
- 面板同时显示「函数执行次数」和「界面显示的数值」,对应「执行渲染」与「提交后界面」两个事实;
- 子组件 C 只更新自己的 state:点它的按钮,只有 C 的计数动,父组件和 A、B 都纹丝不动——更新范围沿数据流向下,不会向上传播。
function ChildWithOwnState() {
const [n, setN] = useState(0); // 自己的状态,更新只影响自己及自己的子组件
return <button onClick={() => setN(n + 1)}>{n}</button>;
}
验证方式:打开浏览器控制台看 log,再用 React DevTools 高亮对照,三个工具的结论应当一致。
动手任务
打开本课实验区,完成三个观察:
- 连点三次父组件 +1,读出两颗子组件的执行次数,说出「父更新 → 子函数重执行」的默认规则。
- 连点三次子组件自己的按钮,确认父组件的执行次数没变。
- 在两颗子组件里各加一行
console.log,对照实验区计数验证你的预测。
常见错误
- 以为 setState 后状态变量立即变新值:当前这次渲染里的还是旧快照(R08 的快照模型),新值在下一轮渲染才可见。
- 把「子组件函数重新执行」当成「DOM 被重新操作」:函数执行只产出虚拟树,DOM 只在 diff 出差异的地方被改;两者成本天差地别。
- 看到子组件重渲染就上 memo:先确认它是否真的昂贵、props 是否真的稳定;盲目 memo 反而增加比较成本。
- 用「界面变了」推断「状态变了」:提交后的界面是唯一事实,判断状态要看渲染函数的输入,而不是屏幕。
检查与复盘
完成下方检查题确认理解。自查清单:
- 能按触发、执行渲染、提交三阶段描述一次更新的完整过程
- 能解释「父更新带动全部子组件函数重执行,但 DOM 只改有差异的部分」
- 验证渲染行为时会用 console.log 与 React DevTools 高亮,而不是猜
参考资料
- React 官方文档:渲染与提交 —— 三个阶段的官方讲解
- React 官方文档:状态如同一张快照 —— 更新请求与快照的关系
读完这一课了?
标记完成后,工作台会安排接下来的学习。