深入理解react的setState

继承棱镜
• 阅读 5125

1.组件挂载图

  • 了解生命周期函数的执行顺序

深入理解react的setState

2.生命周期执行顺序

尝试一下

  • 可以看到在组件在组件初始化时,只执行如下三个方法:

深入理解react的setState

  • 在父组件状态改变时,依次执行的生命周期函数是:

深入理解react的setState

  • 我试着分别在这几个生命周期函数中setState了一下,发现在componentWillUpdate、render、componentDidUpdate 中会报错,也就是说在componentWillUpdate、render、componentDidUpdate中不可以setState。

1.那么问题来了这些周期方法为什么不可以setState
2.setState异步机制,怎么处理,setState(函数)

componentDidMount() {
    SynapseAnalytics.init({ type:Enum.pageTypeEnum.otherPage });
    this.setState({
        val:this.state.val + 1
    });
    //第一次输出  0
    console.log(this.state.val);
    this.setState({
        val:this.state.val + 1
    });
    //第二次输出 0
    console.log(this.state.val);
    setTimeout(()=>{
        this.setState({val:this.state.val + 1});
        //第三次输出 2
        console.log(this.state.val);
        this.setState({
            val:this.state.val + 1
        });
        //第四次输出 3
        console.log(this.state.val);
    }, 0);
} 
  • 依次输出0、0、2、3;因为react并不是setState之后state的值就会改变的,若是这样就太消耗内存了,失去了setState存在的意义。
  • 这里存在一个setstate调用栈的问题,问题来了setState之后都发生了什么
  • 1.this.setState(newState) =>
  • 2.newState存入pending队列 =>
  • 3.调用enqueueUpdate =>
  • 4.是否处于批量更新模式 => 是的话将组件保存到dirtyComponents
  • 5.不是的话遍历dirtyComponents,调用updateComponent,更新pending state or props
  • enqueueUpdate的源码如下:
 function enqueueUpdate(component){
       //injected注入的
       ensureInjected();
       //如果不处于批量更新模式
       if(!batchingStrategy.isBatchingUpdates){
           batchingStrategy.batchedUpdates(enqueueUpdate, component);
           return;
       }
       //如果处于批量更新模式
       dirtyComponents.push(component);
   } 
  • 如果isBatchingUpdates为true,则对所有队列中的更新执行batchedUpdates方法,否则只把当前组件(即调用了setState的组件)放入dirtyComponents数组中,例子中4次setState调用的表现之所以不同,这里的逻辑判断起了关键作用。

参考链接
参考链接

  • 连续调用了多次setState,但是只引发了一次更新生命周期,因为React会将多个this.setState产生的修改放在一个队列里,缓一缓,攒在一起,觉得差不多了在引发一次更新过程。

所以攒的过程中如果你不停的set同一个state的值,只会触发最后一次,例如上面那道题

  • 那么问题又来了:我就想让第三次输出为3,别给我覆盖掉该怎么办?

其实setState还有一个用法,它不止可以接受对象作为参数,还可以接受函数。

3.函数式的setState的用法

直接看代码:

componentDidMount(){
    SynapseAnalytics.init({type:Enum.pageTypeEnum.otherPage});
    this.setState(this.fn.bind(this));
    //第一次输出
    console.log(this.state.val);
    this.setState(this.fn.bind(this));
    //第二次输出
    console.log(this.state.val);
    setTimeout(()=>{
        this.setState({val:this.state.val+1});
        //第三次输出
        console.log(this.state.val);
        this.setState({
            val:this.state.val+1
        });
        //第四次输出
        console.log(this.state.val);
    },0);
}
fn(state,props){
    return{
        val:state.val+1
    }
}
  • 这个函数可以接受两个参数,一个是当前state的值,一个是当前props,这个函数返回一个对象代表对state的修改。
我理解这个state其实就相当于一个全局变量,每次累加的不是this.state,而是state这个变量,所以无论累加多少次,最后将state这个变量赋值给this.state。

注意:在这累加的过程中,若你在函数式的setState方法后面又穿插使用了传统的对象式(this.setState({val:this.state.val + 1}))的话,之前累加的就全白费了,因为上面说过了this.state.val还是0,它只有在render之后才会改变。

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
郜小超 郜小超
4年前
史上最全前端面试题(但是没有答案 自己百度 手动狗头!)
Vue面试题生命周期函数面试题1.什么是vue生命周期2.vue生命周期的作用是什么3.第一次页面加载会触发哪几个钩子4.简述每个周期具体适合哪些场景5.created和mounted的区别6.vue获取数据在哪个周期函数7.请详细说下你对vue生命周期的理解?vue路由面试题1.mvvm框架是什么?2.vuerout
Souleigh ✨ Souleigh ✨
4年前
React - 认识生命周期
这周开始学习React的生命周期。React的生命周期从广义上分为三个阶段:挂载、渲染、卸载.挂载卸载过程constructor()componentWillMount()componentDidMount()componentWillUnmount()更新过程componentWillRece
九路 九路
4年前
Gradle技术之四 - Gradle的Task详解
1Gradle的Task详解1Task定义和配置2Task的执行3Task的依赖和执行顺序4Task类型5Task结合gradle的生命周期6Task实战1.1Task定义和配置1.1.1查看所有的taskjava./gradlewtasks输出Task:tasks
Gwendolyn62 Gwendolyn62
4年前
MySQL的语句执行顺序
今天遇到一个问题就是mysql中insertinto和update以及delete语句中能使用as别名吗?目前还在查看,但是在查阅资料时发现了一些有益的知识,给大家分享一下,就是关于sql以及MySQL语句执行顺序:sql和mysql执行顺序,发现内部机制是一样的。最大区别是在别名的引用上。 一、sql执行顺序 
Souleigh ✨ Souleigh ✨
4年前
React 灵魂 23 问,你能答对几个?
1、setState是异步还是同步?1.合成事件中是异步2.钩子函数中的是异步3.原生事件中是同步4.setTimeout中是同步相关链接:你真的理解setState吗?:2、聊聊react@16.4的生命周期相关连接:React生命周期我对Reactv16.4生命周期的
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
Net Core 页面的生命周期 + OnActionExecuting
十年河东,十年河西,莫欺少年穷学无止境,精益求精NetCore页面的生命周期如下图:!(https://img2020.cnblogs.com/blog/451016/202005/451016202005121751477151221496703.png)具体代码执行顺序如下:控制器基类!(https://images.
Stella981 Stella981
3年前
React生命周期
自从React发布Fiber之后,更新速度日新月异,而生命周期也随之改变,虽然原有的一些生命周期函数面临废弃,但理解其背后更新的机制也是一种学习在这里根据官方文档以及社区上其他优秀的文章进行一个对于生命周期的总结,大致上分为以下三个模块1.新老生命周期的区别2.为什么数据获取要在componentDidMount中进行3.
融云IM即时通讯 融云IM即时通讯
8个月前
融云IM干货丨uni-app 生命周期有哪些?
uniapp的生命周期分为应用生命周期、页面生命周期和组件生命周期三类:应用生命周期应用生命周期函数需要在App.vue中声明,主要包含以下函数:onLaunch:当uniapp初始化完成时触发(全局只触发一次)。onShow:当uniapp启动,或从后台
继承棱镜
继承棱镜
Lv1
我一直在你身后从未离开,只要你能回头
文章
3
粉丝
0
获赞
0