React与Preact中关于setState的区别
源码分析
首先来分析下React以及Preact在setState部分的具体实现。
(太长不看想偷懒,可以直接下翻看结论)
React
关键代码:
setState 阶段:
// ReactUpdateQueue.jsenqueueSetState: function(publicInstance, partialState) { ... var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); queue.push(partialState); enqueueUpdate(internalInstance);}
可以看到React在 setState 的时候不会做任何处理,会把变更直接放到一个专门处理 state 的队列里供组件更新时使用。
更新阶段:
// ReactCompositeComponent.jsupdateComponent: function( transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext,) { var inst = this._instance; ... var willReceive = false; var nextContext; if (this._context === nextUnmaskedContext) { nextContext = inst.context; } else { nextContext = this._processContext(nextUnmaskedContext); willReceive = true; } var prevProps = prevParentElement.props; var nextProps = nextParentElement.props; if (prevParentElement !== nextParentElement) { willReceive = true; } if (willReceive && inst.componentWillReceiveProps) { ... inst.componentWillReceiveProps(nextProps, nextContext); } // 在此处才计算 nextState var nextState = this._processPendingState(nextProps, nextContext); // 此处传入了 nextProps var shouldUpdate = true; if (!this._pendingForceUpdate) { if (inst.shouldComponentUpdate) { ... shouldUpdate = inst.shouldComponentUpdate( nextProps, nextState, nextContext, ); } else { if (this._compositeType === CompositeTypes.PureClass) { // 敲黑板,知识点 —— 如果你的组件没实现shouldComponentUpdate,那么把React.Component 换成 React.PureComponent 可以获得基础版优化,提高性能。 shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); // 浅比较,可以抄去自己改成属性黑/白名单版 } } } ...}// ReactCompositeComponent.js_processPendingState: function(props, context) { // props: nextProps 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 = Object.assign({}, replace ? queue[0] : inst.state); for (var i = replace ? 1 : 0; i < queue.length; i++) { var partial = queue[i]; Object.assign( nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) // nextProps : partial, ); } return nextState;}
通过上面组件更新的流程代码可以看到:
在 updateComponent 中,在 componentWillReceiveProps 之后才会计算 nextState,所以在 componentWillReceiveProps 中 setState 是可以在当次更新中生效的。
在 _processPendingState 会对队列里的 state 进行叠加,如果修改是函数方式,此处传入的state参数是 nextState,props 是 nextProps。
Preact
关键代码:
setState 阶段:
// component.jssetState(state, callback) { let s = this.state; if (!this.prevState) this.prevState = extend({}, s); extend(s, typeof state==='function' ? state(s, this.props) : state); if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback); enqueueRender(this);}
实现的简单粗暴,在 setState 的时候就进行了合并,会立即改写 this.state,在第一次 setState 时会保留 state 状态到 prevState。由于是立即合并state,如果入参state是函数,props 将只是当前 this.props。
更新阶段:
export function renderComponent(component, opts, mountAll, isChild) { ... previousProps = component.prevProps || props, previousState = component.prevState || state, previousContext = component.prevContext || context, ... // if updating if (isUpdate) { component.props = previousProps; component.state = previousState; component.context = previousContext; if (opts!==FORCE_RENDER && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === false) { skip = true; } else if (component.componentWillUpdate) { component.componentWillUpdate(props, state, context); } component.props = props; component.state = state; component.context = context; } ...}
在更新流程前提取了旧 state,shouldComponentUpdate、componentWillUpdate 之后还原回新值,所以在 shouldComponentUpdate 生命周期中,this.props 将获取的是 prevProps,这里与 React 的逻辑并不一致。
划重点
相同点:
在 componentWillReceiveProps 中 setState 都会应用到 nextState。
在 shouldComponentUpdate 中 setState 都不会应用到 nextState,但是可以直接操作传入的 nextState。
不同点:
React下 setState 的值不会立即生效,会一直积累到 componentWillReceiveProps,在此之后会进行合并,并提供给后续生命周期。而Preact下 setState 会立即反映到 this.state,但是,在更新组件的生命周期到 render 前(eg: shouldComponentUpdate), this.state 将会是 prevState。
shouldComponentUpdate 阶段 setState 虽然不会影响到最终 state 的值,但是Preact下会影响 this.state 的值,比如之后 componentWillUpdate 中的 this.state, 总之此阶段不要 setState 反正也没用。
setState 如果使用函数修改,Preact下传入的 props 将会是 prevProps,而React中是 nextProps,在 componentWillReceiveProps 中 setState 时要注意。
总结
如果你写的工程需要同时兼容React及Preact的话:
不要利用React下 setState 在同一次组件更新执行前 state 不立即更新的特性,注意多个 setState 之间是否影响,必要时手动保存旧值。
在组件更新生命周期内,除 componentWillReceiveProps 之外不要使用 setState,提供了 nextState 的生命周期,可以直接修改 nextState。
尽量避免使用 setState 函数修改方式,在 componentWillReceiveProps 中使用时,使用生命周期中的 prevProps(this.props) 和 nextProps。
p.s: antd-mobile 2.0正式版已发布,同时兼容react、preact,轻量、快速、易用的移动端组件库,等你来用~ 【传送门】
以上就是React与Preact中关于setState的区别的详细内容,更多请关注其它相关文章!