资讯专栏INFORMATION COLUMN

揭密React setState

JeOam / 2670人阅读

摘要:下面来逐步的解析图里的流程。一将事务放进队列中这里的可以传也可以传它会产生新的以一种的方式跟旧的进行合并。如果当前不在更新过程的话,则执行更新事务。以上即为的实现过程,最后还是用一个流程图在做一个总结吧参考文档

前言

学过react的人都知道,setState在react里是一个很重要的方法,使用它可以更新我们数据的状态,本篇文章从简单使用深入到setState的内部,全方位为你揭开setState的神秘面纱~

setState的使用注意事项

setState(updater, callback)这个方法是用来告诉react组件数据有更新,有可能需要重新渲染。它是异步的,react通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能,所以这就给我们埋了一个坑:

那就是在使用setState改变状态之后,立刻通过this.state去拿最新的状态往往是拿不到的。

要点一

所以第一个使用要点就是:如果你需要基于最新的state做业务的话,可以在componentDidUpdate或者setState的回调函数里获取。(注:官方推荐第一种做法)

// setState回调函数
changeTitle: function (event) {
  this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
  // Call API with the updated value
}
要点二

设想有一个需求,需要在在onClick里累加两次,如下

  onClick = () => {
    this.setState({ index: this.state.index + 1 });
    this.setState({ index: this.state.index + 1 });
  }

在react眼中,这个方法最终会变成

Object.assign(
  previousState,
  {index: state.index+ 1},
  {index: state.index+ 1},
  ...
)

由于后面的数据会覆盖前面的更改,所以最终只加了一次.所以如果是下一个state依赖前一个state的话,推荐给setState传function

onClick = () => {
    this.setState((prevState, props) => {
      return {quantity: prevState.quantity + 1};
    });
    this.setState((prevState, props) => {
      return {quantity: prevState.quantity + 1};
    });
}

以上是使用setState的两个注意事项,接下来我们来看看setState被调用之后,更新组件的过程,下面是一个简单的流程图。

下面来逐步的解析图里的流程。

一、setState ReactBaseClassses.js
ReactComponent.prototype.setState = function (partialState, callback) {
  //  将setState事务放进队列中
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, "setState");
  }
};

这里的partialState可以传object,也可以传function,它会产生新的state以一种Object.assgine()的方式跟旧的state进行合并。

二、enqueueSetState
  enqueueSetState: function (publicInstance, partialState) {
     // 获取当前组件的instance
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, "setState");
     
     // 将要更新的state放入一个数组里
     var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    queue.push(partialState);
     
     //  将要更新的component instance也放在一个队列里
    enqueueUpdate(internalInstance);
  }

这段代码可以得知,enqueueSetState 做了两件事:
1、将新的state放进数组里
2、用enqueueUpdate来处理将要更新的实例对象

三、enqueueUpdate ReactUpdates.js
function enqueueUpdate(component) {
  // 如果没有处于批量创建/更新组件的阶段,则处理update state事务
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  // 如果正处于批量创建/更新组件的过程,将当前的组件放在dirtyComponents数组中
  dirtyComponents.push(component);
}

由这段代码可以看到,当前如果正处于创建/更新组件的过程,就不会立刻去更新组件,而是先把当前的组件放在dirtyComponent里,所以不是每一次的setState都会更新组件~。

这段代码就解释了我们常常听说的:setState是一个异步的过程,它会集齐一批需要更新的组件然后一起更新

而batchingStrategy 又是个什么东西呢?

四、batchingStrategy ReactDefaultBatchingStrategy.js
var ReactDefaultBatchingStrategy = {
  // 用于标记当前是否出于批量更新
  isBatchingUpdates: false,
  // 当调用这个方法时,正式开始批量更新
  batchedUpdates: function (callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

    ReactDefaultBatchingStrategy.isBatchingUpdates = true;

    // 如果当前事务正在更新过程在中,则调用callback,既enqueueUpdate
    if (alreadyBatchingUpdates) {
      return callback(a, b, c, d, e);
    } else {
    // 否则执行更新事务
      return transaction.perform(callback, null, a, b, c, d, e);
    }
  }
};

这里注意两点:
1、如果当前事务正在更新过程中,则使用enqueueUpdate将当前组件放在dirtyComponent里。
2、如果当前不在更新过程的话,则执行更新事务。

五、transaction
/**
 * 
 *                       wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+
 * 
*/

简单说明一下transaction对象,它暴露了一个perform的方法,用来执行anyMethod,在anyMethod执行的前,需要先执行所有wrapper的initialize方法,在执行完后,要执行所有wrapper的close方法,就辣么简单。

在ReactDefaultBatchingStrategy.js,tranction 的 wrapper有两个 FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }
};

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

可以看到,这两个wrapper的initialize都没有做什么事情,但是在callback执行完之后,RESET_BATCHED_UPDATES 的作用是将isBatchingUpdates置为false,
FLUSH_BATCHED_UPDATES 的作用是执行flushBatchedUpdates,然后里面会循环所有dirtyComponent,调用updateComponent来执行所有的生命周期方法,componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate 最后实现组件的更新。

以上即为setState的实现过程,最后还是用一个流程图在做一个总结吧~

参考文档:

https://zhuanlan.zhihu.com/p/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107937.html

相关文章

  • 揭密“==”隐藏下的类型转换

    摘要:当转换后的原始数据类型为类型或者类型,则继续转换成类型。具体流程图如下备注的数据类型可以分为以下两种原始数据类型才引入的引用类型类型类型转换成类型的规则类型类型标准的数字格式如果是标准的数字格式,转换成类型相比不用多说,比如下面这几个栗子 抛砖引玉 按照正常的逻辑来说,我们判断两个值是否相等会遵循以下规则:showImg(https://segmentfault.com/img/rem...

    guqiu 评论0 收藏0
  • 揭密微信跳一跳小游戏那些外挂

    摘要:所以,我们这个小游戏发布以后,我们就开始花了很多很多时间来打击外挂。二距离判断像素点判断该方法采用自目前最火的跳一跳小游戏辅助程序。 作者:Hahn, 腾讯高级UI工程师商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。 原文链接:http://wetest.qq.com/lab/view/364.html WeTest 导读 张小龙:这个游戏发布以后,其实它的效果有点超...

    lyning 评论0 收藏0
  • setState, forceUpdate, unstable_batchedUpdates看Re

    摘要:是事件回调,有时机执行逻辑,所以为,,是合并的,结束之后被重新设置为。没有控制权的情况有很多回调,网络回调等等。的说明从函数名上理解强制更新。所以可以简单的理解为,只不过这个是不调用自己的声明周期的。 setState同步异步问题,React批量更新一直是一个比较模糊的问题,本文希望从框架设计的角度说明一下这个问题。 React有个UI = f(data) 公式:UI是由data推导...

    Simon_Zhou 评论0 收藏0
  • setState promise 化的探讨 体会 React 团队设计思想

    摘要:我们来从设计思想上,和官方团队的回应上,了解一下否决理由。此外,还有一个方法新的接口设计支持接收一个回调函数,当其子组件挂载时,这个回调函数就会相应触发。 从 setState 那个众所周知的小秘密说起... 在 React 组件中,调用 this.setState() 是最基本的场景。这个方法描述了 state 的变化、触发了组件 re-rendering。但是,也许看似平常的 th...

    caiyongji 评论0 收藏0
  • setState promise 化的探讨 体会 React 团队设计思想

    摘要:我们来从设计思想上,和官方团队的回应上,了解一下否决理由。此外,还有一个方法新的接口设计支持接收一个回调函数,当其子组件挂载时,这个回调函数就会相应触发。 从 setState 那个众所周知的小秘密说起... 在 React 组件中,调用 this.setState() 是最基本的场景。这个方法描述了 state 的变化、触发了组件 re-rendering。但是,也许看似平常的 th...

    forrest23 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<