源码级详解,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 组件。

相关推荐
萌萌哒草头将军5 分钟前
🚀🚀🚀这几个为 vue 设计的 vite 插件,你一定要知道!
前端·vue.js·vite
知识分享小能手8 分钟前
Typescript学习教程,从入门到精通,TypeScript 配置管理与编译器详解(19)
前端·javascript·学习·typescript·前端框架·ecmascript·jquery
比特森林探险记10 分钟前
深入解析Go语言数据类型:从底层到高级应用
java·前端·golang
神秘敲码人10 分钟前
CSS篇-2
前端·css
Dolphin_海豚31 分钟前
augment 无限续杯
前端·aigc·cursor
我血条子呢32 分钟前
[Vue组件]半环进度显示器
前端·javascript·vue.js
啃火龙果的兔子35 分钟前
阻止H5页面中键盘收起的问题
前端
小小小小宇37 分钟前
一文搞定前端隐式类型转换
前端
_r0bin_40 分钟前
前端面试准备-4
前端·javascript·html
鹏多多.1 小时前
vue的监听属性watch的详解
前端·javascript·vue.js·前端框架