React的setState执行机制

1. setState基本特点

1. setState是同步执行的

setState是同步执行的,但是state并不一定会同步更新

2. setState在React生命周期和合成事件中批量覆盖执行

在React的生命周期钩子和合成事件中,多次执行setState,会批量执行

具体表现为,多次同步执行的setState只有最后一次起作用,前面的全被覆盖

当遇到多个setState调用时候,会提取单次传递setState的对象,把他们合并在一起形成一个新的
单一对象,并用这个单一的对象去做setState的事情,就像Object.assign的对象合并,后一个
key值会覆盖前面的key值

const a = {name : ‘kong‘, age : ‘17‘}
const b = {name : ‘fang‘, sex : ‘men‘}
Object.assign({}, a, b);
//{name : ‘fang‘, age : ‘17‘, sex : ‘men‘}

name被后面的覆盖了,但是age和sex都起作用了

例如:

class Hello extends React.Component {
    constructor(){
      super();
      this.state = {
        name: ‘aa‘
    }
  }
  componentWillMount(){
      this.setState({
        name: ‘aa‘ + 1
    });
    console.log(this.state.name); //aa
    this.setState({
        name: ‘aa‘ + 1
    });
    console.log(this.state.name); //aa
  }
  render() {
    return <div>
      <div>Hello {this.props.name}</div>
      <div>Hello {this.state.name}</div>
    </div>;
  }
}

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById(‘container‘)
);

componentWillMount中两个log均为初始状态aa,而render中的state.name则为aa2
componentWillMount中的setState均执行了,但是state的更新是延迟的,所以log出的state均为aa
而render中的state.name则在state更新之后,而且只有第二次的aa1起了作用

3. setState在原生事件,setTimeout,setInterval,Promise等异步操作中,state会同步更新

异步操作中setState,即使在React的钩子或合成事件中,state都不会批量更新,而是会同步更新,
多次连续操作setState,每次都会re-render,state会同步更新

2. setState的形式

setState(object,[callback]) //对象式,object为nextState
setState(function,[callback]) //函数式,function为(prevState,props) => stateChange

[callback]则为state更新之后的回调,此时state已经完成更新,可以取到更新后的state
[callback]是在setState之后,更准确来说是当正式执行batchUpdate队列的state更新完成后就会执行,不是在re-rendered之后

使用者两者形式的setState,state的更新都是异步的,但是多次连续使用函数式的setState,
React本身会进行一个递归传递调用,将上一次函数执行后的state传给下一个函数,因此每次执行
setState后能读取到更新后的state值。

如果对象式和函数式的setState混合使用,则对象式的会覆盖前面无论函数式还是对象式的任何setState,
但是不会影响后面的setState。

例如:

function increment(state,props){
    return {count: state.count + 1};
}

function incrementMultiple(){
    this.setState(increment);
    this.setState(increment);
    this.setState({count: this.state.count + 1});
    this.setState(increment);
}

上面三个函数式的setState中间插入一个对象式的setState,则最后的结果是2,而不是4,
因为对象式的setState将前面的任何形式的setState覆盖了,但是后面的setState依然起作用

3. setState的基本过程

setState的调用会引起React的更新寿命周期的4个函数执行。

shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

当shouldComponentUpdate执行时,返回true,进行下一步,this.state没有被更新
返回false,停止,更新this.state

当componentWillUpdate被调用时,this.state也没有被更新

直到render被调用时候,this.state才被更新。

总之,直到下一次render函数调用(或者下一次shouldComponentUpdate返回false时)才能得到更新后的this.state
因此获取更新后的状态可以有3种方法:

1. setState函数式

2. setState在setTimeout,Promise等异步中执行

setStatePromise(updator) {
    return new Promise(((resolve, reject) => {
        this.setState(updator, resolve);
    }));
}

componentWillMount() {
    this.setStatePromise(({ num }) => ({
        num: num + 1,
    })).then(() => {
        console.log(this.state.num);
    });
}

或者

function setStateAsync(nextState){
  return new Promise(resolve => {
    this.setState(nextState, resolve);
  });
}

async func() {
  ...
  await this.setStateAsync({count: this.state.count + 1});
  await this.setStateAsync({count: this.state.count + 1});
}

3. setState callback

setState({
    index: 1
}}, function(){
    console.log(this.state.index);
})

4. componentDidUpdate

componentDidUpdate(){
    console.log(this.state.index);
}

4. setState批量更新的过程

在React的生命周期和合成事件执行前后都有相应的钩子,分别是pre钩子和post钩子,pre钩子会调用batchedUpdate方法将isBatchingUpdates变量置为true,开启批量更新,而post钩子会将isBatchingUpdates置为false

如下图所示:

isBatchingUpdates变量置为true,则会走批量更新分支,setState的更新会被存入队列中,待同步代码执行完后,再执行队列中的state更新。

而在原生事件和异步操作中,不会执行pre钩子,或者生命周期的中的异步操作之前执行了pre钩子,但是pos钩子也在异步操作之前执行完了,isBatchingUpdates必定为false,也就不会进行批量更新。

5. setState的缺点

1. setState有可能循环调用

调用setState之后,shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate 等生命周期函数会依次被调用(如果shouldComponentUpdate没有返回 false的话),如果我们在render、componentWillUpdate或componentDidUpdate中调用了setState方法,那么可能会造成循环调用,最终导致浏览器内存占满后崩溃

2、setState可能会引发不必要的渲染

可能造成不必要渲染的因素如下:
(1)新 state 和之前的一样。这种情况可以通过 shouldComponentUpdate 解决。
(2)state 中的某些属性和视图没有关系(譬如事件、timer ID等),这些属性改变不影响视图的显示。

3、setState并不总能有效地管理组件中的所有状态

因为组件中的某些属性是和视图没有关系的,当组件变得复杂的时候可能会出现各种各样的状态需要管理,这时候用setState管理所有状态是不可取的。state中本应该只保存与渲染有关的状态,而与渲染无关的状态尽量不放在state中管理,可以直接保存为组件实例的属性,这样在属性改变的时候,不会触发渲染,避免浪费

6. setState和replaceState的区别

setState是修改其中的部分状态,相当于Object.assign,只是覆盖,不会减少原来的状态
replaceState是完全替换原来的状态,相当于赋值,将原来的state替换为另一个对象,如果新状态属性减少,那么state中就没有这个状态了

7. 一个实例分析

上图的执行结果为 0 0 1 1 3 4

参考: http://www.360doc.com/content/17/0803/18/27576111_676420051.shtml
    https://blog.csdn.net/kongjunchao159/article/details/72626637
    https://blog.csdn.net/michellezhai/article/details/80098211
    https://www.cnblogs.com/danceonbeat/p/6993674.html
    https://segmentfault.com/a/1190000010682761
    https://segmentfault.com/a/1190000015821018

原文地址:https://www.cnblogs.com/mengff/p/9611614.html

时间: 2024-11-09 08:56:41

React的setState执行机制的相关文章

React的setState分析

前端框架层出不穷,不过万变不离其宗,就是从MVC过渡到MVVM.从数据映射到DOM,angular中用的是watcher对象,vue是观察者模式,react就是state了. React通过管理状态实现对组件的管理,通过this.state()方法更新state.当this.setState()被调用的时候,React会重新调用render方法来重新渲染UI. 本文针对React的SetState的源码来进行解读,根据陈屹老师的<深入React技术栈>加上自己的理解. 1. setState异

React: React的组件状态机制

一.简介 在React中,有两个核心的默认属性,分别是state和props.state会记录组件的状态,React根据状态的变化,会对界面做相应的调整或渲染.props则是数据流向属性,React通过props传递来实现父子组件之间的通信.本篇主要研究React的组件状态机制,在很多Web界面可以看到数据不停的变化,其实,这个过程就是React监听到state状态在不停地发生改变时一次次重新对组件重新渲染的结果.基于React这个机制,所以开发者可以很灵活地用state来完成对行为的控制.数据

React中setState同步更新策略

本文和大家分享的主要是React中setState同步更新相关内容,希望对大家学习React有所帮助. 为了提高性能React将setState设置为批次更新,即是异步操作函数,并不能以顺序控制流的方式设置某些事件,我们也不能依赖于 this.state 来计算未来状态.典型的譬如我们希望在从服务端抓取数据并且渲染到界面之后,再隐藏加载进度条或者外部加载提示: componentDidMount() { fetch('https://example.com') .then((res) => re

React中setState 什么时候是同步的,什么时候是异步的?

class Example extends React.Component { constructor() { super(); this.state = { val: 0 }; } componentDidMount() { this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 1 次 log this.setState({val: this.state.val + 1}); console.lo

【Spark Core】任务执行机制和Task源码浅析1

引言 上一小节<TaskScheduler源码与任务提交原理浅析2>介绍了Driver侧将Stage进行划分,根据Executor闲置情况分发任务,最终通过DriverActor向executorActor发送任务消息. 我们要了解Executor的执行机制首先要了解Executor在Driver侧的注册过程,这篇文章先了解一下Application和Executor的注册过程. 1. Task类及其相关 1.1 Task类 Spark将由Executor执行的Task分为ShuffleMap

【Spark Core】任务执行机制和Task源码浅析2

引言 上一小节<任务执行机制和Task源码浅析1>介绍了Executor的注册过程. 这一小节,我将从Executor端,就接收LaunchTask消息之后Executor的执行任务过程进行介绍. 1. Executor的launchTasks函数 DriverActor提交任务,发送LaunchTask指令给CoarseGrainedExecutorBackend,接收到指令之后,让它内部的executor来发起任务,即调用空闲的executor的launchTask函数. 下面是Coars

JVM--类执行机制与JVM内存组成结构

类执行机制 JVM基于栈体系结构来执行class字节码,线程被创建后,产生程序计数器(PC)和栈(Stack) PC存放下一条执行的指令在方法内的偏移量,Stack存放一个栈帧,每个栈帧对应每个方法的每次调用,栈帧中存放局部变量和操作数栈 栈的结构如下图: JVM内存结构 Java虚拟机规范规定的java虚拟机内存其实就是java虚拟机运行时数据区,其架构如下: 堆 所有通过new创建的对象的内存都在堆中进行分配: 栈 每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操

PostgreSQL执行机制的初步学习

作为开源数据库的新手,近日有兴对比了Pg和MySQL的查询计划. 通过Pg源码目录下的src\backend\executor\README文件,加上一些简单调试,就能对Pg的执行机制产生一个初步印象:而MySQL的代码可读性比Pg差了不少,可能还要花些时日去了解先. 原本想写一篇执行机制对比的文章,现在只能谈谈对Pg的体会,不足和错误之处敬请指正. Pg算是学院派的开源数据库代表产品,其基于关系代数的优化.操作符的实现看起来十分亲切.相较于MySQL扁平的计划,Pg的执行计划让人一目了然. P

【JS】JavaScript引擎的内部执行机制

 近期在复习JavaScript,看到setTimeout函数时.想起曾经刚学时,在一本书上看过setTimeout()里的回调函数执行的间隔时间有昌不是后面设置的值.曾经没想太多.网上看了JS大神的解释,整理记录下JavaScript引擎的内部执行机制. 首先看一段小程序: <script> alert('第1'); setTimeout(function(){alert('第2');}, 2000); alert('第3'); </script> 输出顺序是:第1.第3,第