news 2026/6/10 3:49:59

小白都能看懂的setState原理,一次吃透!!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小白都能看懂的setState原理,一次吃透!!

react中执行setState更新状态时都做了什么?

结论:setState是react中修改组件状态、触发视图更新的一个api。他是一个异步更新的过程,但是不同于setTimeout、promise等异步Api实现,而是react自身的批量更新机制导致的异步,去触发react的更新流程(协调、渲染),从而实现的组件状态更新和视图的重新渲染。整个过程涉及,状态更新队列、自动批处理、调度协调机制等,具体流程请看下图(大概搂一眼现在不了解没事)

为什么是异步?看案例

核心表现:状态不会立即更新

class Example extends React.Component { state = { count: 0 }; handleClick = () => { console.log('点击前:', this.state.count); // 0 this.setState({ count: this.state.count + 1 }); console.log('点击后:', this.state.count); // 还是 0! }; }

多个state执行什么表现?

handleClick = () => { this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); // 如果 count 初始是 0,最终只会变成 1,而不是 3 // 因为三次都基于同一个 this.state.count (0),引出概率自动批处理!!!这很重要!!! };

关键点setState调用后,this.state不会马上改变。React 会将多个setState调用合并(batch),在事件处理函数结束时统一更新。这就是上述说的是异步但是不同于setTimeout、promise等异步Api实现,而是react自身的批量更新机制(自动批处理)导致的异步原因。

什么是批量更新机制(自动批处理)?

这里的批量更新机制(自动批处理)需要分成react17及之前和react17后去理解,两者在批量更新时稍微有点不一样。

在 React 17 及之前,React 会判断当前是否处于批量更新上下文(合成事件、生命周期钩子)。如果是,则将多个setState合并到一次渲染中处理;如果不是(setTimeoutPromise、原生事件),则每次setState都会触发一次独立的渲染调度,但状态本身仍然是异步更新的,不会立即同步反映到 state上。React 18默认在所有场景下开启自动批处理,只要使用createRoot()挂载,即使在setTimeout或 Promise 中多次setState也只会触发一次渲染

看代码(有条件的同学可以自己在控制台打印看看)

一、合成事件中的打印(React 17 和 18 表现一致)不知道什么是合成事件的,可以去找资料看看,或者我后期会出。
import React, { Component } from 'react'; class Example extends Component { state = { count: 0 }; handleClick = () => { console.log('=== 合成事件开始 ==='); console.log('1. setState 前:', this.state.count); // 0 this.setState({ count: this.state.count + 1 }); console.log('2. 第1次 setState 后:', this.state.count); // 0 this.setState({ count: this.state.count + 1 }); console.log('3. 第2次 setState 后:', this.state.count); // 0 this.setState({ count: this.state.count + 1 }); console.log('4. 第3次 setState 后:', this.state.count); // 0 console.log('=== 合成事件结束 ==='); }; render() { console.log('render:', this.state.count); return ( <button onClick={this.handleClick}> 合成事件点击 (count: {this.state.count}) </button> ); } } === 合成事件开始 === 1. setState 前: 0 2. 第1次 setState 后: 0 3. 第2次 setState 后: 0 4. 第3次 setState 后: 0 === 合成事件结束 === render: 1
二、setTimeout 中的打印(React 17 vs 18 差异)
1、React 17 表现(立即执行,非批量更新)
class Example extends Component { state = { count: 0 }; handleClick = () => { setTimeout(() => { console.log('=== setTimeout 开始 ==='); console.log('1. setState 前:', this.state.count); // 0 this.setState({ count: this.state.count + 1 }); console.log('2. 第1次 setState 后:', this.state.count); // 1 ← 已更新! this.setState({ count: this.state.count + 1 }); console.log('3. 第2次 setState 后:', this.state.count); // 2 ← 已更新! this.setState({ count: this.state.count + 1 }); console.log('4. 第3次 setState 后:', this.state.count); // 3 ← 已更新! console.log('=== setTimeout 结束 ==='); }, 0); }; render() { console.log('render:', this.state.count); return <button onClick={this.handleClick}>setTimeout 点击</button>; } } === setTimeout 开始 === 1. setState 前: 0 2. 第1次 setState 后: 1 render: 1 3. 第2次 setState 后: 2 render: 2 4. 第3次 setState 后: 3 render: 3 === setTimeout 结束 ===
2、React 18 表现(自动批处理)
class Example extends Component { state = { count: 0 }; handleClick = () => { setTimeout(() => { console.log('=== setTimeout 开始 ==='); console.log('1. setState 前:', this.state.count); // 0 this.setState({ count: this.state.count + 1 }); console.log('2. 第1次 setState 后:', this.state.count); // 0 ← 未变! this.setState({ count: this.state.count + 1 }); console.log('3. 第2次 setState 后:', this.state.count); // 0 ← 未变! this.setState({ count: this.state.count + 1 }); console.log('4. 第3次 setState 后:', this.state.count); // 0 ← 未变! console.log('=== setTimeout 结束 ==='); }, 0); }; render() { console.log('render:', this.state.count); return <button onClick={this.handleClick}>setTimeout 点击</button>; } } 1. setState 前: 0 2. 第1次 setState 后: 0 3. 第2次 setState 后: 0 4. 第3次 setState 后: 0 === setTimeout 结束 === render: 1
3、核心对比总结

4、思考,为什么同样的代码react17和18为什么控制台表现不一致?
  • 上述我们提到了,在 React 17 及之前,React 会判断当前是否处于批量更新上下文(合成事件、生命周期钩子)。如果是,则将多个setState合并到一次渲染中处理;如果不是(setTimeoutPromise、原生事件),则每次setState都会触发一次独立的渲染调度,但状态本身仍然是异步更新的,不会立即同步反映到this.state上。React 18默认在所有场景下开启自动批处理,只要使用createRoot()挂载,即使在setTimeout或 Promise 中多次setState也只会触发一次渲染所以react17和18不一致!

现在我们再看,在调用setState时,react内部都经历了什么?

setState的本质是:创建 Update → 加入 Fiber 的 UpdateQueue → 请求调度 → 批量合并 → 工作循环遍历 Fiber 树计算新状态 → Diff 标记副作用 → Commit 阶段同步更新 DOM → 执行回调和 Effect→更新完成

  1. 结合图看
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 3:47:15

ICRA 2026 灵巧手五强争霸:直驱、绳驱、混驱,谁在定义下一代机器人的手

ICRA 2026 灵巧手五强争霸:直驱、绳驱、混驱,谁在定义下一代机器人的手? 一、为什么灵巧手突然卷起来了 2026 年 ICRA 的展区有个肉眼可见的变化:灵巧手展台的数量比上一届翻了至少一倍。舞肌科技、曦诺未来、临界点 AGILINK、灵心巧手、灵巧智能、源升智能——六家中国企…

作者头像 李华
网站建设 2026/6/10 3:44:40

技术文章大纲:Codex安装适配国产信创环境

技术文章大纲&#xff1a;Codex安装适配国产信创环境国产信创环境概述信创产业背景与核心目标&#xff08;自主可控、安全可靠&#xff09;主流国产化平台介绍&#xff08;如麒麟OS、统信UOS、龙芯、飞腾等&#xff09;适配国产环境的常见技术挑战&#xff08;硬件兼容性、软件…

作者头像 李华
网站建设 2026/6/10 3:43:42

SPDX+Syft+Policy引擎打造合规流水线

发散创新&#xff1a;用 SPDXSyftCustom Policy Engine 构建可审计、可落地的开源合规流水线 在企业级软件交付中&#xff0c;开源合规已不再是法务部门的“事后检查单”&#xff0c;而是研发流程中必须前置嵌入的硬性质量门禁。据 Linux Foundation 2023 年《Open Source Com…

作者头像 李华
网站建设 2026/6/10 3:40:38

N100软路由(五) 成型与加固--AP模式Mesh组网与网络优化

成型与加固 – AP 模式、Mesh 组网与网络优化N100 软路由家庭网络改造系列&#xff08;五&#xff09;拨号通了&#xff0c;端口的坑也踩过了。这篇把剩下的活全干完&#xff1a;WTA301 改 AP、Mesh、速度测试、MTU、光猫持久访问、IPv6。 WTA301 改 AP TP-LINK 有个方便的特性…

作者头像 李华