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

源码分析

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

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

React

关键代码:

setState 阶段:

  1. // ReactUpdateQueue.js
  2. enqueueSetState: function(publicInstance, partialState) {
  3. ...
  4. var queue =
  5. internalInstance._pendingStateQueue ||
  6. (internalInstance._pendingStateQueue = []);
  7. queue.push(partialState);
  8. enqueueUpdate(internalInstance);
  9. }

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

更新阶段:

  1. // ReactCompositeComponent.js
  2. updateComponent: function(
  3. transaction,
  4. prevParentElement,
  5. nextParentElement,
  6. prevUnmaskedContext,
  7. nextUnmaskedContext,
  8. ) {
  9. var inst = this._instance;
  10. ...
  11. var willReceive = false;
  12. var nextContext;
  13. if (this._context === nextUnmaskedContext) {
  14. nextContext = inst.context;
  15. } else {
  16. nextContext = this._processContext(nextUnmaskedContext);
  17. willReceive = true;
  18. }
  19. var prevProps = prevParentElement.props;
  20. var nextProps = nextParentElement.props;
  21. if (prevParentElement !== nextParentElement) {
  22. willReceive = true;
  23. }
  24. if (willReceive && inst.componentWillReceiveProps) {
  25. ...
  26. inst.componentWillReceiveProps(nextProps, nextContext);
  27. }
  28. // 在此处才计算 nextState
  29. var nextState = this._processPendingState(nextProps, nextContext); // 此处传入了 nextProps
  30. var shouldUpdate = true;
  31. if (!this._pendingForceUpdate) {
  32. if (inst.shouldComponentUpdate) {
  33. ...
  34. shouldUpdate = inst.shouldComponentUpdate(
  35. nextProps,
  36. nextState,
  37. nextContext,
  38. );
  39. } else {
  40. if (this._compositeType === CompositeTypes.PureClass) { // 敲黑板,知识点 —— 如果你的组件没实现shouldComponentUpdate,那么把React.Component 换成 React.PureComponent 可以获得基础版优化,提高性能。
  41. shouldUpdate =
  42. !shallowEqual(prevProps, nextProps) ||
  43. !shallowEqual(inst.state, nextState); // 浅比较,可以抄去自己改成属性黑/白名单版
  44. }
  45. }
  46. }
  47. ...
  48. }
  49. // ReactCompositeComponent.js
  50. _processPendingState: function(props, context) { // props: nextProps
  51. var inst = this._instance;
  52. var queue = this._pendingStateQueue;
  53. var replace = this._pendingReplaceState;
  54. this._pendingReplaceState = false;
  55. this._pendingStateQueue = null;
  56. if (!queue) {
  57. return inst.state;
  58. }
  59. if (replace && queue.length === 1) {
  60. return queue[0];
  61. }
  62. var nextState = Object.assign({}, replace ? queue[0] : inst.state);
  63. for (var i = replace ? 1 : 0; i < queue.length; i++) {
  64. var partial = queue[i];
  65. Object.assign(
  66. nextState,
  67. typeof partial === 'function'
  68. ? partial.call(inst, nextState, props, context) // nextProps
  69. : partial,
  70. );
  71. }
  72. return nextState;
  73. }

通过上面组件更新的流程代码可以看到:

  • 在 updateComponent 中,在 componentWillReceiveProps 之后才会计算 nextState,所以在 componentWillReceiveProps 中 setState 是可以在当次更新中生效的。
  • 在 _processPendingState 会对队列里的 state 进行叠加,如果修改是函数方式,此处传入的state参数是 nextState,props 是 nextProps。

Preact

关键代码:

setState 阶段:

  1. // component.js
  2. setState(state, callback) {
  3. let s = this.state;
  4. if (!this.prevState) this.prevState = extend({}, s);
  5. extend(s, typeof state==='function' ? state(s, this.props) : state);
  6. if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback);
  7. enqueueRender(this);
  8. }

实现的简单粗暴,在 setState 的时候就进行了合并,会立即改写 this.state,在第一次 setState 时会保留 state 状态到 prevState。由于是立即合并state,如果入参state是函数,props 将只是当前 this.props。

更新阶段:

  1. export function renderComponent(component, opts, mountAll, isChild) {
  2. ...
  3. previousProps = component.prevProps || props,
  4. previousState = component.prevState || state,
  5. previousContext = component.prevContext || context,
  6. ...
  7. // if updating
  8. if (isUpdate) {
  9. component.props = previousProps;
  10. component.state = previousState;
  11. component.context = previousContext;
  12. if (opts!==FORCE_RENDER
  13. && component.shouldComponentUpdate
  14. && component.shouldComponentUpdate(props, state, context) === false) {
  15. skip = true;
  16. }
  17. else if (component.componentWillUpdate) {
  18. component.componentWillUpdate(props, state, context);
  19. }
  20. component.props = props;
  21. component.state = state;
  22. component.context = context;
  23. }
  24. ...
  25. }

在更新流程前提取了旧 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. 201521123100 《Java程序设计》 第2周学习总结

    一. 本章学习总结 1.本周学习了Java语言中各种数据类型以及运算符,其中大部分还是和c语言差不多,发现了各种语言的相通性 2.进一步学习了eclipse的功能和使用方法,学会了如何将其与码云连接更 ...

  2. ubuntu下php不能显示中文的问题的解决过程。

    在阿里的ECS上的ubuntu平台上成功的安装了apache2和php5与mysql,并进行了测试. 如图所示:

  3. 移动商城第八篇【添加商品之基本属性和大字段数据(FCK文本编辑器)】

    添加商品 修改对应的超链接url,controller转发到对应的JSP页面 <a href="${path}/item/toAddItem.do" class=" ...

  4. SSM整合开发

    导入开发包 asm-3.2.0.RELEASE.jar asm-3.3.1.jar c3p0-0.9.jar cglib-2.2.2.jar com.springsource.net.sf.cglib ...

  5. maven web 项目中启动报错 Java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet

    主要原因是maven项目里面的jar包吗,没有导入到项目中 maven web 项目中启动报错 Java.lang.ClassNotFoundException: org.springframewor ...

  6. 杂谈--DML触发器学习

    触发器按类型分为三类: 1. DML 触发器,在数据变更时触发: 2. DDL 触发器,在修改数据库级别或实例级别对象时触发: 3. Login 触发器,在用户登录时触发: 最常见的是DML触发器,D ...

  7. Codeforce E. Fire

    E. Fire time limit per test 2 seconds memory limit per test 256 megabytes input standard input outpu ...

  8. Day2 基本数据类型

    一.python数据类型 1.1数字 2 是一个整数的例子. 长整数 不过是大一些的整数. 3.23和52.3E-4是浮点数的例子.E标记表示10的幂.在这里,52.3E-4表示52.3 * 10-4 ...

  9. 模糊搜索神器fzf

    前言 fzf是目前最快的fuzzy finder.使用golang编写.结合其他工具(比如ag和fasd)可以完成非常多的工作. 让你通过输入模糊的关键词就可以定位文件或文件夹.当你的思维也习惯了模糊 ...

  10. 即时通信系统Openfire分析之五:会话管理

    什么是会话? A拨了B的电话 电话接通 A问道:Are you OK? B回复:I have a bug! A挂了电话 这整个过程就是会话. 会话(Session)是一个客户与服务器之间的不中断的请求 ...