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. 201521123112《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点 1.2 使用常规方法总结其他上课内容 关于多态,多态性就是相同的形态,不同的定义.在简单点说的话就是不同类的对象对同一个消息作出的相应 ...

  2. Java 最常用类(前100名)来自一万个开源项目

    大部分的 Java 软件开发都会使用到各种不同的库.近日我们从一万个开源的 Java 项目中进行分析,从中提取出最常用的 Java 类,这些类有来自于 Java 的标准库,也有第三方库.每个类在同一个 ...

  3. bookStore第二篇【图书模块、前台页面】

    图书模块 分析 在设计图书管理的时候,我们应该想到:图书和分类是有关系的.一个分类可以对应多本图书. 为什么要这样设计?这样更加人性化,用户在购买书籍的时候,用户能够查看相关分类后的图书,而不是全部图 ...

  4. ExtJS4为form表单必填项添加红色*标识

    通常情况下,ExtJS4的form表单必填项在输入状态下会有特殊提示,非输入状态下,显示却和其他项没有任何区别.为使必填项更加容易区分,我们需要根据allowBlank的属性值,为form表单中的必填 ...

  5. System.getProperty()参数大全

    System.getProperty()获取Java各种配置属性,参数如下: Java.version Java 运行时环境版本 java.vendor Java 运行时环境供应商 java.vend ...

  6. Struts框架2

    1.框架:是一个半成品,可以在其基础上在次开发. 2.struts2框架:它是一个web层使用的mvc框架. 3.struts2核心 1.struts2核心 2.xwork核心 4.struts2入门 ...

  7. SDP开发

    1.1 前言 在企业间的商业竞争越来越激烈的今天,如何快速实现客户需求,如果快速方开发.修改.更新系统功能,如何降低软件研发的成本等等,在此目标基础上研发了软件快速开发(SDP)工具.通过平台设计器快 ...

  8. appium整理文档

    from appium import webdriver import time,unittest,HTMLTestRunner class Testlogin(unittest.TestCase): ...

  9. TCP/IP中你不得不知的十大秘密

    这段时间 有一点心很浮躁,不过希望自己马上要矫正过来.好好学习编程!这段时间我想好好地研究一下TCP/IP协议和网络传输这块!加油 一.TCP/IP模型 TCP/IP协议模型(Transmission ...

  10. Quartz学习——SSMM(Spring+SpringMVC+Mybatis+Mysql)和Quartz集成详解(四)

    当任何时候觉你得难受了,其实你的大脑是在进化,当任何时候你觉得轻松,其实都在使用以前的坏习惯. 通过前面的学习,你可能大致了解了Quartz,本篇博文为你打开学习SSMM+Quartz的旅程!欢迎上车 ...