当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 深入研究React中setState源码

深入研究React中setState源码

2017年12月08日  | 移动技术网IT编程  | 我要评论
react作为一门前端框架,虽然只是focus在mvvm中的view部分,但还是实现了view和model的绑定。修改数据的同时,可以实现view的刷新。这大大简化了我们的

react作为一门前端框架,虽然只是focus在mvvm中的view部分,但还是实现了view和model的绑定。修改数据的同时,可以实现view的刷新。这大大简化了我们的逻辑,只用关心数据流的变化,同时减少了代码量,使得后期维护也更加方便。这个特性则要归功于setstate()方法。react中利用队列机制来管理state,避免了很多重复的view刷新。下面我们来从源码角度探寻下setstate机制。

1 还是先声明一个组件,从最开始一步步来寻源;

class app extends component {
  //只在组件重新加载的时候执行一次
  constructor(props) {
    super(props);
   //..
  }
   //other methods
}
//reactbaseclasses.js中如下:这里就是setstate函数的来源;
//super其实就是下面这个函数
function reactcomponent(props, context, updater) {
 this.props = props;
 this.context = context;
 this.refs = emptyobject;
 // we initialize the default updater but the real one gets injected by the
 // renderer.
 this.updater = updater || reactnoopupdatequeue;
}
reactcomponent.prototype.setstate = function (partialstate, callback) {
 this.updater.enqueuesetstate(this, partialstate);
 if (callback) {
  this.updater.enqueuecallback(this, callback, 'setstate');
 }
};

所以主要来看是否传入了updater参数,也就是说何时进行 new 组件;具体的updater参数是怎么传递进来的,以及是那个对象,参见

react源码分析系列文章下面的react中context updater到底是如何传递的

这里直接说结果,updater对象其实就是reactupdatequeue.js 中暴漏出的reactupdatequeue对象;

2 既然找到了setstate之后执行的动作,我们在一步步深入进去

class root extends react.component {
 constructor(props) {
  super(props);
  this.state = {
   count: 0
  };
 }
 componentdidmount() {
  let me = this;
  me.setstate({
   count: me.state.count + 1
  });
  console.log(me.state.count);  // 打印出0
  me.setstate({
   count: me.state.count + 1
  });
  console.log(me.state.count);  // 打印出0
  settimeout(function(){
   me.setstate({
    count: me.state.count + 1
   });
   console.log(me.state.count);  // 打印出2
  }, 0);
  settimeout(function(){
   me.setstate({
    count: me.state.count + 1
   });
   console.log(me.state.count);  // 打印出3
  }, 0);
 }
 render() {
  return (
   <h1>{this.state.count}</h1>
  )
 }
}

reactcomponent.prototype.setstate = function (partialstate, callback) {
 this.updater.enqueuesetstate(this, partialstate);
 if (callback) {
  this.updater.enqueuecallback(this, callback, 'setstate');
 }
};

reactupdatequeue.js

var reactupdates = require('./reactupdates');

function enqueueupdate(internalinstance) {
 reactupdates.enqueueupdate(internalinstance);
};
function getinternalinstancereadyforupdate(publicinstance, callername) {
 //在reactcompositecomponent.js中有这样一行代码,这就是其来源;
 // store a reference from the instance back to the internal representation
  //reactinstancemap.set(inst, this);
 var internalinstance = reactinstancemap.get(publicinstance);
 //返回的是在reactcompositecomponent.js中construct函数返回的对象;reactinstance实例对象并不是简单的new 我们写的组件的实例对象,而是经过instantiatereactcomponent.js中reactcompositecomponentwrapper函数包装的对象;详见 创建react组件方式以及源码分析.md
 return internalinstance;
};
var reactupdatequeue = {
//。。。。省略其他代码
 enqueuecallback: function (publicinstance, callback, callername) {
  reactupdatequeue.validatecallback(callback, callername);
  var internalinstance = getinternalinstancereadyforupdate(publicinstance);
  if (!internalinstance) {
   return null;
  }
//这里将callback放入组件实例的_pendingcallbacks数组中;
  if (internalinstance._pendingcallbacks) {
   internalinstance._pendingcallbacks.push(callback);
  } else {
   internalinstance._pendingcallbacks = [callback];
  }
  // todo: the callback here is ignored when setstate is called from
  // componentwillmount. either fix it or disallow doing so completely in
  // favor of getinitialstate. alternatively, we can disallow
  // componentwillmount during server-side rendering.
  enqueueupdate(internalinstance);
 },

 enqueuesetstate: function (publicinstance, partialstate) {
  var internalinstance = getinternalinstancereadyforupdate(publicinstance, 'setstate');
  if (!internalinstance) {
   return;
  }
  //这里,初始化queue变量,同时初始化 internalinstance._pendingstatequeue = [ ] ;
  //对于 || 的短路运算还是要多梳理下
  //queue数组(模拟队列)中存放着setstate放进来的对象;
  var queue = internalinstance._pendingstatequeue || (internalinstance._pendingstatequeue = []);
  //这里将partialstate放入queue数组中,也就是internalinstance._pendingstatequeue 数组中,此时,每次setstate的partialstate,都放进了react组件实例对象上的_pendingstatequeue属性中,成为一个数组;
  queue.push(partialstate);

  enqueueupdate(internalinstance);
 },
};

module.exports = reactupdatequeue;

可以看到enqueuesetstate enqueuecallback 最后都会执行enqueueupdate;

function enqueueupdate(internalinstance) {
 reactupdates.enqueueupdate(internalinstance);
}

reactupdates.js

var dirtycomponents = [];
var updatebatchnumber = 0;
var asapcallbackqueue = callbackqueue.getpooled();
var asapenqueued = false;
//这里声明batchingstrategy为null,后期通过注册给其赋值;
var batchingstrategy = null;
//这里的component参数是js中reactcompositecomponentwrapper函数包装的后的react组件实例对象;
function enqueueupdate(component) {
 ensureinjected();
//第一次执行setstate的时候,可以进入if语句,遇到里面的return语句,终止执行
 //如果不是正处于创建或更新组件阶段,则处理update事务

 if (!batchingstrategy.isbatchingupdates) {
  //batchedupdates就是reactdefaultbatchingstrategy.js中声明的
  batchingstrategy.batchedupdates(enqueueupdate, component);
  return;
 }
//第二次执行setstate的时候,进入不了if语句,将组件放入dirtycomponents
 //如果正在创建或更新组件,则暂且先不处理update,只是将组件放在dirtycomponents数组中

 dirtycomponents.push(component);
 if (component._updatebatchnumber == null) {
  component._updatebatchnumber = updatebatchnumber + 1;
 }
};
//enqueueupdate包含了react避免重复render的逻辑。mountcomponent和updatecomponent方法在执行的最开始,会调用到batchedupdates进行批处理更新,此时会将isbatchingupdates设置为true,也就是将状态标记为现在正处于更新阶段了。之后react以事务的方式处理组件update,事务处理完后会调用wrapper.close(), 而transaction_wrappers中包含了reset_batched_updates这个wrapper,故最终会调用reset_batched_updates.close(), 它最终会将isbatchingupdates设置为false。

reactdefaultbatchingstrategy.js

//reset_batched_updates用来管理isbatchingupdates的状态
var reset_batched_updates = {
 initialize: emptyfunction,
 close: function () {
  // 事务批更新处理结束时,将isbatchingupdates设为了false
  reactdefaultbatchingstrategy.isbatchingupdates = false;
 }
};
//flush_batched_updates会在一个transaction的close阶段运行runbatchedupdates,从而执行update。
//因为close的执行顺序是flush_batched_updates.close ==> 然后reset_batched_updates.close
var flush_batched_updates = {
 initialize: emptyfunction,
 close: reactupdates.flushbatchedupdates.bind(reactupdates)
};

var transaction_wrappers = [flush_batched_updates, reset_batched_updates];

function reactdefaultbatchingstrategytransaction() {
 this.reinitializetransaction();
}

_assign(reactdefaultbatchingstrategytransaction.prototype, transaction, {
 gettransactionwrappers: function () {
  return transaction_wrappers;
 }
});
//这个transition就是下面reactdefaultbatchingstrategy对象中使用的transaction变量
var transaction = new reactdefaultbatchingstrategytransaction();
var reactdefaultbatchingstrategy = {
 isbatchingupdates: false,

 /**
  * call the provided function in a context within which calls to `setstate`
  * and friends are batched such that components aren't updated unnecessarily.
  */
 batchedupdates: function (callback, a, b, c, d, e) {
  var alreadybatchingupdates = reactdefaultbatchingstrategy.isbatchingupdates;
// 批处理最开始时,将isbatchingupdates设为true,表明正在更新
  reactdefaultbatchingstrategy.isbatchingupdates = true;

  // the code is written this way to avoid extra allocations
  if (alreadybatchingupdates) {
   return callback(a, b, c, d, e);
  } else {
   //transition在上面已经声明; // 以事务的方式处理updates,后面详细分析transaction
   return transaction.perform(callback, null, a, b, c, d, e);
  }
 }
};

module.exports = reactdefaultbatchingstrategy;

接下来我们看下react中的事物处理机制到底是如何运行的;

transaction.js

var _prodinvariant = require('./reactprodinvariant');
var invariant = require('fbjs/lib/invariant');
var observed_error = {};
var transactionimpl = {
 reinitializetransaction: function () {
  //gettransactionwrappers这个函数reactdefaultbatchingstrategy.js中声明的,上面有;返回一个数组;
  this.transactionwrappers = this.gettransactionwrappers();
  if (this.wrapperinitdata) {
   this.wrapperinitdata.length = 0;
  } else {
   this.wrapperinitdata = [];
  }
  this._isintransaction = false;
 },

 _isintransaction: false,
 gettransactionwrappers: null,
 isintransaction: function () {
  return !!this._isintransaction;
 },
 perform: function (method, scope, a, b, c, d, e, f) {
  var errorthrown;
  var ret;
  try {
   this._isintransaction = true;
   errorthrown = true;
   //var transaction_wrappers = [flush_batched_updates, reset_batched_updates];
   //1 这里会先执行所有的transaction_wrappers中成员的initialize方法,上面声明的其都是emptyfunction
   this.initializeall(0);
   //2 这里其实还是执行的 enqueueupdate 函数
   ret = method.call(scope, a, b, c, d, e, f);
   errorthrown = false;
  } finally {
   try {
    if (errorthrown) {
     // if `method` throws, prefer to show that stack trace over any thrown
     // by invoking `closeall`.
     try {
      this.closeall(0);
     } catch (err) {}
    } else {
     // since `method` didn't throw, we don't want to silence the exception
     // here.
     //3 执行transaction_wrappers对象中成员的所有close方法;
     this.closeall(0);
    }
   } finally {
    this._isintransaction = false;
   }
  }
  return ret;
 },

 initializeall: function (startindex) {
  var transactionwrappers = this.transactionwrappers;
  for (var i = startindex; i < transactionwrappers.length; i++) {
   var wrapper = transactionwrappers[i];
   try {
    
    this.wrapperinitdata[i] = observed_error;
    this.wrapperinitdata[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
   } finally {
    if (this.wrapperinitdata[i] === observed_error) {
     
     try {
      this.initializeall(i + 1);
     } catch (err) {}
    }
   }
  }
 },
 closeall: function (startindex) {
  var transactionwrappers = this.transactionwrappers;
  for (var i = startindex; i < transactionwrappers.length; i++) {
   var wrapper = transactionwrappers[i];
   var initdata = this.wrapperinitdata[i];
   var errorthrown;
   try {
  
    errorthrown = true;
    if (initdata !== observed_error && wrapper.close) {
     wrapper.close.call(this, initdata);
    }
    errorthrown = false;
   } finally {
    if (errorthrown) {
    
     try {
      this.closeall(i + 1);
     } catch (e) {}
    }
   }
  }
  this.wrapperinitdata.length = 0;
 }
};

module.exports = transactionimpl

//3 执行transaction_wrappers对象中成员的所有close方法;
var flush_batched_updates = {
 initialize: emptyfunction,
 close: reactupdates.flushbatchedupdates.bind(reactupdates)
};

接着会执行reactupdates.js中的flushbatchedupdates方法

reactupdates.js中

var flushbatchedupdates = function () {
 
 while (dirtycomponents.length || asapenqueued) {
  if (dirtycomponents.length) {
   var transaction = reactupdatesflushtransaction.getpooled();
   //这里执行runbatchedupdates函数;
   transaction.perform(runbatchedupdates, null, transaction);
   reactupdatesflushtransaction.release(transaction);
  }

  if (asapenqueued) {
   asapenqueued = false;
   var queue = asapcallbackqueue;
   asapcallbackqueue = callbackqueue.getpooled();
   queue.notifyall();
   callbackqueue.release(queue);
  }
 }
};
function runbatchedupdates(transaction) {
 var len = transaction.dirtycomponentslength;
 
 dirtycomponents.sort(mountordercomparator);

 updatebatchnumber++;

 for (var i = 0; i < len; i++) {
 
  var component = dirtycomponents[i];
  var callbacks = component._pendingcallbacks;
  component._pendingcallbacks = null;

  var markername;
  if (reactfeatureflags.logtoplevelrenders) {
   var namedcomponent = component;
   // duck type toplevelwrapper. this is probably always true.
   if (component._currentelement.type.isreacttoplevelwrapper) {
    namedcomponent = component._renderedcomponent;
   }
   markername = 'react update: ' + namedcomponent.getname();
   console.time(markername);
  }
//这里才是真正的开始更新组件
  reactreconciler.performupdateifnecessary(component, transaction.reconciletransaction, updatebatchnumber);

  if (markername) {
   console.timeend(markername);
  }

  if (callbacks) {
   for (var j = 0; j < callbacks.length; j++) {
    transaction.callbackqueue.enqueue(callbacks[j], component.getpublicinstance());
   }
  }
 }
}

reactreconciler.js中

performupdateifnecessary: function (internalinstance, transaction, updatebatchnumber) {
  if (internalinstance._updatebatchnumber !== updatebatchnumber) {
   // the component's enqueued batch number should always be the current
   // batch or the following one.
   return;
  }
 //这里执行react组件实例对象的更新;internalinstance上的performupdateifnecessary在reactcompositecomponent.js中的;
  internalinstance.performupdateifnecessary(transaction);
  if (process.env.node_env !== 'production') {
   if (internalinstance._debugid !== 0) {
    reactinstrumentation.debugtool.onupdatecomponent(internalinstance._debugid);
   }
  }
 }

reactcompositecomponent.js

performupdateifnecessary: function (transaction) {
 if (this._pendingelement != null) {
  // receivecomponent会最终调用到updatecomponent,从而刷新view
  reactreconciler.receivecomponent(this, this._pendingelement, transaction, this._context);
 } else if (this._pendingstatequeue !== null || this._pendingforceupdate) {
  // 执行updatecomponent,从而刷新view。
  this.updatecomponent(transaction, this._currentelement, this._currentelement, this._context, this._context);
 } else {
  this._updatebatchnumber = null;
 }
},

 //执行更新react组件的props. state。context函数

 updatecomponent: function (transaction, prevparentelement, nextparentelement, prevunmaskedcontext, nextunmaskedcontext) {
  var inst = this._instance;
  var willreceive = false;
  var nextcontext;
  // determine if the context has changed or not
  if (this._context === nextunmaskedcontext) {
   nextcontext = inst.context;
  } else {
   nextcontext = this._processcontext(nextunmaskedcontext);
   willreceive = true;
  }

  var prevprops = prevparentelement.props;
  var nextprops = nextparentelement.props;

  // not a simple state update but a props update
  if (prevparentelement !== nextparentelement) {
   willreceive = true;
  }

  // an update here will schedule an update but immediately set
  // _pendingstatequeue which will ensure that any state updates gets
  // immediately reconciled instead of waiting for the next batch.
  if (willreceive && inst.componentwillreceiveprops) {
   if (process.env.node_env !== 'production') {
    measurelifecycleperf(function () {
     return inst.componentwillreceiveprops(nextprops, nextcontext);
    }, this._debugid, 'componentwillreceiveprops');
   } else {
    inst.componentwillreceiveprops(nextprops, nextcontext);
   }
  }
//这里可以知道为什么setstate可以接受函数,主要就是_processpendingstate函数;
  //这里仅仅是将每次setstate放入到_pendingstatequeue队列中的值,合并到nextstate,并没有真正的更新state的值;真正更新组件的state的值是在下面;
  var nextstate = this._processpendingstate(nextprops, nextcontext);
  var shouldupdate = true;

  if (!this._pendingforceupdate) {
   if (inst.shouldcomponentupdate) {
    if (process.env.node_env !== 'production') {
     shouldupdate = measurelifecycleperf(function () {
      return inst.shouldcomponentupdate(nextprops, nextstate, nextcontext);
     }, this._debugid, 'shouldcomponentupdate');
    } else {
     shouldupdate = inst.shouldcomponentupdate(nextprops, nextstate, nextcontext);
    }
   } else {
    if (this._compositetype === compositetypes.pureclass) {
     shouldupdate = !shallowequal(prevprops, nextprops) || !shallowequal(inst.state, nextstate);
    }
   }
  }

  this._updatebatchnumber = null;
  if (shouldupdate) {
   this._pendingforceupdate = false;
   // will set `this.props`, `this.state` and `this.context`.
   this._performcomponentupdate(nextparentelement, nextprops, nextstate, nextcontext, transaction, nextunmaskedcontext);
  } else {
   // if it's determined that a component should not update, we still want
   // to set props and state but we shortcut the rest of the update.
   //诺:在这里更新组件的state. props 等值;
   this._currentelement = nextparentelement;
   this._context = nextunmaskedcontext;
   inst.props = nextprops;
   inst.state = nextstate;
   inst.context = nextcontext;
  }
 },


_processpendingstate: function (props, context) {
 var inst = this._instance;
 var queue = this._pendingstatequeue;
 var replace = this._pendingreplacestate;
 this._pendingreplacestate = false;
 this._pendingstatequeue = null;

 if (!queue) {
  return inst.state;
 }

 if (replace && queue.length === 1) {
  return queue[0];
 }

 var nextstate = _assign({}, replace ? queue[0] : inst.state);
 for (var i = replace ? 1 : 0; i < queue.length; i++) {
  var partial = queue[i];
  //如果是setstate的参数是一个函数,那么该函数接受三个参数,分别是state props context
  _assign(nextstate, typeof partial === 'function' ? partial.call(inst, nextstate, props, context) : partial);
 }

 return nextstate;
},

this.state的更新会在_processpendingstate执行完执行。所以两次setstate取到的都是this.state.count最初的值0,这就解释了之前的现象。其实,这也是react为了解决这种前后state依赖但是state又没及时更新的一种方案,因此在使用时大家要根据实际情况来判断该用哪种方式传参。来看个小例子直观感受下

handleclickonlikebutton () {
  this.setstate({ count: 0 }) // => this.state.count 还是 undefined
  this.setstate({ count: this.state.count + 1}) // => undefined + 1 = nan
  this.setstate({ count: this.state.count + 2}) // => nan + 2 = nan
 }
//....vs ....
handleclickonlikebutton () {
  this.setstate((prevstate) => {
   return { count: 0 }
  })
  this.setstate((prevstate) => {
   return { count: prevstate.count + 1 } // 上一个 setstate 的返回是 count 为 0,当前返回 1
  })
  this.setstate((prevstate) => {
   return { count: prevstate.count + 2 } // 上一个 setstate 的返回是 count 为 1,当前返回 3
  })
  // 最后的结果是 this.state.count 为 3
 }
...

setstate流程还是很复杂的,设计也很精巧,避免了重复无谓的刷新组件。它的主要流程如下

  1. enqueuesetstate将state放入队列中,并调用enqueueupdate处理要更新的component
  2. 如果组件当前正处于update事务中,则先将component存入dirtycomponent中。否则调用batchedupdates处理。
  3. batchedupdates发起一次transaction.perform()事务
  4. 开始执行事务初始化,运行,结束三个阶段
    1. 初始化:事务初始化阶段没有注册方法,故无方法要执行
    2. 运行:执行setsate时传入的callback方法,一般不会传callback参数
    3. 结束:更新isbatchingupdates为false,并执行flush_batched_updates这个wrapper中的close方法
  5. flush_batched_updates在close阶段,会循环遍历所有的dirtycomponents,调用updatecomponent刷新组件,并执行它的pendingcallbacks, 也就是setstate中设置的callback。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网