深入React知识点整理(二)
承接上文,深入React知识点整理(一)
7.React 生命周期React 的主要思想是通过构建可复用组件来构建用户界面。所谓组件,其实就是有限状态机(FSM),通过状态渲染对应的界面,且每个组件都有自己的生命周期,它规定了组件的状态和方法需要在哪个阶段改变和执行。 组件的生命周期在不同状态下的执行顺序:
constructor 中的 this.state = {} 其实就是调用内部的 getInitialState 方法。
自定义组件(ReactCompositeComponent)的生命周期主要通过 3 个阶段进行管理——MOUNTING、RECEIVE_PROPS 和 UNMOUNTING,它们负责通知组件当前所处的阶段,应该执行生命周期中的哪个步骤。
最后,一张图归纳React生命周期,以及不同生命周期中setState的使用情况: 这里主要介绍了React生命周期执行顺序,以及在不同生命周期内使用setState的情况,重点在于合适使用setState。 8.setState调用栈React中的setState是个很著名的方法,当你需要更改state触发页面重绘的时候,调用setSatet方法。但是setState并不会立刻执行你的更改,这也是刚开始使用React时很苦恼的一件事。 经过一段时间的使用和学习,知道了React的setState是'异步'的:
官方给出的回答是React为了优化性能,会合并多次setState操作,并计算出最终值再执行,所以setState看起来是'异步'的。这种想法很好,我们可以借鉴到项目开发中去,但是代码层面是如何实现的?在任何地方调用setState都会是’异步的吗‘? setState源码: // 更新 state ReactComponent.prototype.setState = function (partialState,callback) { this.updater.enqueueSetState(this,partialState); if (callback) { this.updater.enqueueCallback(this,callback,'setState'); } }; ... enqueueSetState: function (publicInstance,partialState) { var internalInstance = getInternalInstanceReadyForUpdate( publicInstance,'setState' ); if (!internalInstance) { return; } // 更新队列合并操作 var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); enqueueUpdate(internalInstance); },... // 如果存在 _pendingElement、_pendingStateQueue和_pendingForceUpdate,则更新组件 performUpdateIfNecessary: function (transaction) { if (this._pendingElement != null) { ReactReconciler.receiveComponent(this,this._pendingElement,transaction,this._context); } if (this._pendingStateQueue !== null || this._pendingForceUpdate) { this.updateComponent(transaction,this._currentElement,this._context,this._context); } } 通过上面源码大致可以看出,setState函数执行的时候,把被更新的state放入到 问题现在全在 到这里可以看到很清楚地看到,setState并不是全部都是’异步的‘,当组件处于非批量更新模式的时候,是立即更新的。enqueueUpdate源码: function enqueueUpdate(component) { ensureInjected(); // 如果不处于批量更新模式 if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate,component); return; } // 如果处于批量更新模式,则将该组件保存在 dirtyComponents 中 dirtyComponents.push(component); } batchingStrategy代码如下: var ReactDefaultBatchingStrategy = { isBatchingUpdates: false,batchedUpdates: function(callback,a,b,c,d,e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; if (alreadyBatchingUpdates) { callback(a,e); } else { transaction.perform(callback,null,e); } },} 通过代码来看, 这里还有一个概念:事务。 事务就是将需要执行的方法使用 wrapper 封装起来,再通过事务提供的 perform 方法执行。而在 perform 之前,先执行所有 wrapper 中的 initialize 方法,执行完 perform 之后(即执行method 方法后)再执行所有的 close 方法。一组 initialize 及 close 方法称为一个 wrapper。从图3-16中可以看出,事务支持多个 wrapper 叠加。 屏幕快照 2017-12-07 下午8.28.26.png 那事务又跟setState有什么关系,我们举个例子: import React,{ Component } from 'react'; class Example extends Component { constructor() { super(); this.state = { val: 0 }; } componentDidMount() { this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 1 次输出 this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 2 次输出 setTimeout(() => { this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 3 次输出 this.setState({val: this.state.val + 1}); console.log(this.state.val); // 第 4 次输出 },0); } render() { return null; } } 这个例子中, 屏幕快照 2017-12-07 下午9.20.54.png 通过截图可以看到,我们在调用setState的之后,打印输出了事务结束的closeAll,这说明 接下来的解释就顺理成章了,因为在 componentDidMount 中调用 setState 时,batchingStrategy的 isBatchingUpdates 已经被设为 true,所以两次 setState 的结果并没有立即生效,而是被放进了 dirtyComponents 中。这也解释了两次打印 this.state.val 都是 0 的原因,因为新的 state 还没有被应用到组件中。而setTimeout函数中的setState并没有在事务中,所以立即执行。 所以这里得到一个结论:在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。 React引发的事件处理有很多,都是我们常用的比如所有的生命周期函数(在生命周期函数中可以使用setState的),React代理的事件;这些函数应用场景很高,让我们误以为setState一直是'异步'的。 所以,我们也可以得出解决setState异步的方法:
当然了,还有一个比较决绝的办法,就是不用setState。 相关文章可以看看程墨老师的文章:setState为什么不会同步更新组件状态。 总结React算是一个颠覆式的UI框架,有很多知识点值得学习,深入的研究一些问题对于实际使用上也会有一定帮助。本文也是根据《深入React技术栈》前几章内容摘录,整理的知识点,有兴趣可以看看原著。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |