源码级详解,React 如何利用 setState 进行数据管理的:

setState 是干嘛的

  1. 在 React 中,状态被认为是可读的,我们应始终替换它,而不是直接修改它。
  2. this.state 通常是用来初始化 state 数据的,this.setState 是修改 state 的。
  3. 直接修改 state 是不会触发视图变化的,调用 setState 传入新对象用于修改。
  4. 代码分析:
kotlin 复制代码
this.setState({
    key1: newState1,
    key2: newState2, //将要设置的新状态
    ...
}, callback) // 第二个参数是 state 更新完成后的回调函数

setState 的调用原理

  1. 调用流程图展示:
  1. 源码 级详解:

    1. 进入 setState 这个入口函数后,根据有无回调函数,进入不同的处理队列
    kotlin 复制代码
       ReactComponent.prototype.setState = function (partialState, callback) {
         this.updater.enqueueSetState(this, partialState);
         if (callback) {
           this.updater.enqueueCallback(this, callback, 'setState');
         }
       };
    1. enqueueSetState 方法是通过拿到组件实例中的 state 数组,将其放到状态队列里,并调用 enqueueUpdate 方法来处理接下来要更新的对象
    2. 在 enqueueUpdate 方法中引出了一个 "锁管理器"------ batchingStrategy,这里的 "锁" 指的是全局唯一的 isBatchingUpdates 变量,这个锁决定了是走更新流程,还是应该排队等待。当等到该状态更新时,调用 batchedUpdates 方法来直接立即更新组件。这个全局变量初始值是 "false",意味着队列中没有数据要去更新,即可以直接调用 batchedUpdate 去执行更新操作,执行该更新操作时,再把 "锁" 给锁上(将isBatchingUpdates置为true)。
js 复制代码
 enqueueSetState: function (publicInstance, partialState) {
               // 根据 this 拿到对应的组件实例
               var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
               // 这个 queue 对应的就是一个组件实例的 state 数组
               var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
               queue.push(partialState);
               //  enqueueUpdate 用来处理当前的组件实例
               enqueueUpdate(internalInstance);
             }
js 复制代码
.      function enqueueUpdate(component) {
        ensureInjected();
        // 注意这一句是问题的关键,isBatchingUpdates标识着当前是否处于批量创建/更新组件的阶段
        if (!batchingStrategy.isBatchingUpdates) {
        // 若当前没有处于批量创建/更新组件的阶段,则立即更新组件
        batchingStrategy.batchedUpdates(enqueueUpdate, component);
        return;
        }
        // 否则,先把组件塞入 dirtyComponents 队列里,让它"再等等"
        dirtyComponents.push(component);
        if (component.\_updateBatchNumber == null) {
        component.\_updateBatchNumber = updateBatchNumber + 1;
        }
        }
        .
  1. 额外思考: React 中的 setState 批量更新的过程是什么?

    当我们处理状态高频改变这种场景,调用 setState 时,出于性能原因,state不会立即改变,根据批处理队列,将多次 setState 的状态修改合并成一次状态修改,最终只产生一次组件渲染。

setState 调用之后发生了什么?是同步还是异步的?

  1. setState 调用之后发生了什么:

    首先,会将传入的参数和组件当前状态合并,触发调和过程。

    其次,在调和过程中,React 会根据新状态构建 React 状态树。

    然后,React 得到元素树后,会自动计算新老树的节点差异。根据这些差异算法,能精确定位到哪些位置发生改变,保证了按需更新。

    最后,根据改变位置重新渲染整个页面。

    注:如果短时间内,频繁更新 setState,会将那些新 state 压入栈中,在合适的时机实现批量更新,达到提升性能的效果。

  2. setState 是同步的还是异步的:

    同步状态:每执行一次 setState 时,都会进行 vnode diff + dom 修改。

    异步状态:可以通过一个同步代码实现多个 setState 合并成一次组件更新。

    总的来说, setState 不是单纯的同步/异步的,这取决于它的调用场景。 源码 来说, 通过 isBatchingUpdates 来判断 setState 是先排队还是直接更新,如果要排队(true),那就执行异步操作,不排队就直接更新。以具体适用场景来说, 在 React 可以控制的地方就走异步设计,在 React 无法控制的地方比如原生事件,就只能同步更新。

State 是怎么注入到组件的,从 reducer 到组件经历了什么样的过程

  1. 通过 connect 和 mapStateToProps 将state注入到组件中

  2. reducer 到组件经历了什么样的过程:

    Reducer 对 action 对象进行管理,更新组件状态,将新的状态值返回 store 。

    通过 connect(mapStateToProps,mapDispatchToProps)高阶组件,此时将状态值从 store 取出并作为 props 参数传递到组件

    源码实现:

    javascript 复制代码
     import React from 'react'
        import PropTypes from 'prop-types'
    
        // 高阶组件 contect 
        export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
            class Connect extends React.Component {
                // 通过对context调用获取store
                static contextTypes = {
                    store: PropTypes.object
                }
    
                constructor() {
                    super()
                    this.state = {
                        allProps: {}
                    }
                }
    
                // 第一遍需初始化所有组件初始状态
                componentWillMount() {
                    const store = this.context.store
                    this._updateProps()
                    store.subscribe(() => this._updateProps()); // 加入_updateProps()至store里的监听事件列表
                }
    
                // 执行action后更新props,使组件可以更新至最新状态(类似于setState)
                _updateProps() {
                    const store = this.context.store;
                    let stateProps = mapStateToProps ?
                        mapStateToProps(store.getState(), this.props) : {} // 防止 mapStateToProps 没有传入
                    let dispatchProps = mapDispatchToProps ?
                        mapDispatchToProps(store.dispatch, this.props) : {
                                            dispatch: store.dispatch
                                        } // 防止 mapDispatchToProps 没有传入
                    this.setState({
                        allProps: {
                            ...stateProps,
                            ...dispatchProps,
                            ...this.props
                        }
                    })
                }
    
                render() {
                    return <WrappedComponent {...this.state.allProps} />
                }
            }
            return Connect
        }

React 中的 props 和 state 有什么区别

区别 props state
特性 只读性和不可变性Props 是传递给组件的 只能在constructor中初始化,是组件的私有属性只能通过组件内部的 this.setState来修改
作用 父组件向子组件传递数据 组件保存,控制以及修改自己的状态

总结

setState 通过声明式状态管理驱动 UI 更新,其异步合并机制优化了渲染效率。开发者需注意函数式更新、嵌套对象合并及副作用处理,以构建高效可靠的 React 组件。

相关推荐
web小白成长日记10 小时前
企业级 Vue3 + Element Plus 主题定制架构:从“能用”到“好用”的进阶之路
前端·架构
APIshop10 小时前
Python 爬虫获取 item_get_web —— 淘宝商品 SKU、详情图、券后价全流程解析
前端·爬虫·python
风送雨10 小时前
FastMCP 2.0 服务端开发教学文档(下)
服务器·前端·网络·人工智能·python·ai
XTTX11010 小时前
Vue3+Cesium教程(36)--动态设置降雨效果
前端·javascript·vue.js
LYFlied11 小时前
WebGPU与浏览器边缘智能:开启去中心化AI新纪元
前端·人工智能·大模型·去中心化·区块链
Setsuna_F_Seiei11 小时前
2025 年度总结:人生重要阶段的一年
前端·程序员·年终总结
model200511 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
han_12 小时前
从一道前端面试题,谈 JS 对象存储特点和运算符执行顺序
前端·javascript·面试
aPurpleBerry12 小时前
React 01 目录结构、tsx 语法
前端·react.js
jayaccc12 小时前
微前端架构实战全解析
前端·架构