Preact是React的轻量级实现,是React比较好的替代者之一,有着体积小的优点,当然与React之间一定会存在实现上的差异,本文介绍了在 setState 方面的差异之处。

源码分析

首先来分析下React以及Preact在setState部分的具体实现。

(太长不看想偷懒,可以直接下翻看结论)

React

关键代码:

setState 阶段:

// ReactUpdateQueue.js
enqueueSetState: function(publicInstance, partialState) {
... var queue =
internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
queue.push(partialState); enqueueUpdate(internalInstance);
}

可以看到React在 setState 的时候不会做任何处理,会把变更直接放到一个专门处理 state 的队列里供组件更新时使用。

更新阶段:

// ReactCompositeComponent.js
updateComponent: 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.js
setState(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的更多相关文章

  1. React源码解析:setState

    先来几个例子热热身: ......... constructor(props){ super(props); this.state = { index: 0 } } componentDidMount ...

  2. (文章也有问题,请自行跳过)react中的状态机每次setState都是重新创建新的对象,如需取值,应该在render中处理。

    demo如下 class Demo4StateLearn extends React.Component { constructor(props) { super(props); this.state ...

  3. [React] How to use a setState Updater Function with a Reducer Pattern

    In this lesson we'll walk through setting up an updater function that can receive an action argument ...

  4. [React] Pass a function to setState in React

    In React, when you want to set the state which calculation depends on the current state, using an ob ...

  5. react的setState使用中遇到的问题

    setState()更新的数据和自己预期的不一致 对 React 新手来说,使用 setState 是一件很复杂的事情.即使是熟练的 React 开发,也很有可能因为 React 的一些机制而产生一些 ...

  6. 深入理解 React JS 中的 setState

    此文主要探讨了 React JS 中的 setState 背后的机制,供深入学习 React 研究之用. 在课程 React.js入门基础与案例开发 中,有些同学会发现 React JS 中的 set ...

  7. react 源码之setState

    今天看了react源码,仅以记录. 1:monorepo (react 的代码管理方式) 与multirepo 相对. monorepo是单代码仓库, 是把所有相关项目都集中在一个代码仓库中,每个mo ...

  8. React中this.setState是同步还是异步?为什么要设计成异步?

    在使用react的时候,this.setState为什么是异步呢? 一直以来没有深思这个问题.昨天就此问题搜索了一下. react创始人之一 Dan Abramovgaearon在GitHub上回答了 ...

  9. [Web 前端] 我不再使用React.setState的3个原因

    copy from : https://blog.csdn.net/smk108/article/details/85237838 从几个月前开始,我在新开发的React组件中不再使用setState ...

随机推荐

  1. 201521123110《Java程序设计》第1周学习总结

    1.第一周学习总结 `本周开始了对java的初次学习接触,Java是一门新的编程语言不同于C,由于有了c的基础,对于Java的理解和学习也相对从前学C更容易些. 也学习了Java的诞生发展以及运用包括 ...

  2. 201521123023《Java程序设计》第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. (1)File可以表示一个特定文件的名称,也能表示一个目录下的一组文件的名称. (2)继承自InputStre ...

  3. Java:final、static关键字 详解+两者结合使用

    一  final关键字 1) 关于final的重要知识点 final关键字可以用于成员变量.本地变量.方法以及类. final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误. ...

  4. hadoop运行wordcount实例,hdfs简单操作

    1.查看hadoop版本 [hadoop@ltt1 sbin]$ hadoop version Hadoop -cdh5.12.0 Subversion http://github.com/cloud ...

  5. 易语言 【寻找文本】命令的bug

    最近在重写易语言模块的时候,在取子文本操作时老是出错,经常出现一些奇怪的问题,一开始以为是代码问题,可是找半天硬是找不到问题所在. 于是进入了找bug模式,这么几行代码,看了我半个小时,左改右改,总感 ...

  6. org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML doc

    今天在Spring中换了一种配置bean的方式,发现报错了Unexpected exception parsing XML document from class path resource , 经过 ...

  7. java 线程一

    java基础学习总结--线程(一) 一.线程的基本概念 线程理解:线程是一个程序里面不同的执行路径 每一个分支都叫做一个线程,main()叫做主分支,也叫主线程. 程只是一个静态的概念,机器上的一个. ...

  8. AngularJS–Scope(作用域)

    点击查看AngularJS系列目录 转载请注明出处:http://www.cnblogs.com/leosx/ Scope Scope 是一个应用程序的模块的对象.它是表达式的执行上下文.它充斥在DO ...

  9. 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ---干净的终止一个线程

    干净的终止一个线程  我曾经在第2章产生一个后台线程,用以输出一张屏幕外的 bitmap 图.我们必须解决的一个最复杂的问题就是,如果用户企图结束程序,而这张bitmap 图尚未完成,怎么办?第2章的 ...

  10. Rundeck部署和基本使用

    rundeck 介绍 Rundeck 是一款能在数据中心或云环境中的日常业务中使程序自动化的开源软件.Rundeck 提供了大量功能,可以减轻耗时繁重的体力劳动.团队可以相互协作,分享如何过程自动化, ...