深入研究React中setState源码

网友投稿 358 2023-03-16


深入研究React中setState源码

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 (

)

}

}

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 getInternSOJBdECMqalInstanceReadyForUpdate(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;

trySOJBdECMq {

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流程还是很复杂的,设计也很精巧,避免了重复无谓的刷新组件。它的主要流程如下

enqueueSetState将state放入队列中,并调用enqueueUpdate处理要更新的Component

如果组件当前正处于update事务中,则先将Component存入dirtyComponent中。否则调用batchedUpdates处理。

batchedUpdates发起一次transaction.perform()事务

开始执行事务初始化,运行,结束三个阶段

初始化:事务初始化阶段没有注册方法,故无方法要执行

运行:执行setSate时传入的callback方法,一般不会传callback参数

结束:更新isBatchingUpdates为false,并执行FLUSH_BATCHED_UPDATES这个wrapper中的close方法

FLUSH_BATCHED_UPDATES在close阶段,会循环遍历所有的dirtyComponents,调用updateComponent刷新组件,并执行它的pendingCallbacks, 也就是setState中设置的callback。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:api接口文档模板 网盘(接口api怎么写)
下一篇:根据接口写测试用例(接口的测试用例一般都怎么写)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~