React源码解析之setState和forceUpdate

一、enqueueSetState() 非异步方法中,无论调用多少个setState,它们都会在最后一次setState后,放入更新队列,然后执行一次统一的更新,详情请参考: React.setState之state批处理的机制为什么React.setState是异步的?

作用:React节点的fiber对象创建update,并将该更新对象入队

源码:

//classComponent初始化的时候拿到的update对象
const classComponentUpdater = {
  isMounted,
  enqueueSetState(inst, payload, callback) {
    //inst即调用this.setState时传进来的this
    //也就是classComponent实例

    //通过this获取fiber对象
    //this._reactInternalFiber
    //this本身有存储 fiber对象 的属性,叫 _reactInternalFiber
    const fiber = getInstance(inst);
    //计算当前时间,之前讲过 不讲了
    const currentTime = requestCurrentTime();
    //异步加载的设置,暂时不讲
    const suspenseConfig = requestCurrentSuspenseConfig();
    //计算fiber对象的过期时间
    const expirationTime = computeExpirationForFiber(
      currentTime,
      fiber,
      suspenseConfig,
    );
    //创建update对象
    const update = createUpdate(expirationTime, suspenseConfig);
    //setState传进来的要更新的对象
    update.payload = payload;
    //callback就是setState({},()=>{})的回调函数
    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'setState');
      }
      update.callback = callback;
    }
    //暂时不管
    if (revertPassiveEffectsChange) {
      flushPassiveEffects();
    }
    //update入队
    enqueueUpdate(fiber, update);
    //任务调度
    scheduleWork(fiber, expirationTime);
  },
};

解析: (1)getInstance

//getInstance
export function get(key) {
  return key._reactInternalFiber;
}

就是获取目标对象的_reactInternalFiber属性,即this.setState中的this

(2)requestCurrentTime,请见:React源码解析之ReactDOM.render()

(3)computeExpirationForFiber,请见:React源码解析之ExpirationTime

(4)createUpdate,请见:React源码解析之Update和UpdateQueue

(5)注意下payloadpayload就是setState传进来的要更新的对象

this.setState({a:1},callback) 中的 {a:1} 即 payload
//====================
update.payload = payload;

(6)enqueueUpdate,请见:React源码解析之Update和UpdateQueue

(7)scheduleWork,篇幅较长,会放在下篇讲。

二、enqueueForceUpdate() 作用: 强制让组件重新渲染,也是给React节点的fiber对象创建update,并将该更新对象入队

源码:

  enqueueForceUpdate(inst, callback) {
    const fiber = getInstance(inst);
    const currentTime = requestCurrentTime();
    const suspenseConfig = requestCurrentSuspenseConfig();
    const expirationTime = computeExpirationForFiber(
      currentTime,
      fiber,
      suspenseConfig,
    );

    const update = createUpdate(expirationTime, suspenseConfig);
    //与setState不同的地方
    //默认是0更新,需要改成2强制更新
    update.tag = ForceUpdate;

    if (callback !== undefined && callback !== null) {
      if (__DEV__) {
        warnOnInvalidCallback(callback, 'forceUpdate');
      }
      update.callback = callback;
    }

    if (revertPassiveEffectsChange) {
      flushPassiveEffects();
    }
    enqueueUpdate(fiber, update);
    scheduleWork(fiber, expirationTime);
  },

解析:enqueueSetState()方法的流程类似,唯一不同的是多了个手动修改属性tag的值:

//与setState不同的地方
//默认是0更新,需要改成2强制更新
update.tag = ForceUpdate;

可以看到createUpdate()方法中,初始化的tag值是UpdateState

//创建update对象
export function createUpdate(
  expirationTime: ExpirationTime,
  suspenseConfig: null | SuspenseConfig,
): Update<*> {
  return {
    // export const UpdateState = 0;
    // export const ReplaceState = 1;
    // export const ForceUpdate = 2;
    // export const CaptureUpdate = 3;

    //重点提下CaptureUpdate,在React16后有一个ErrorBoundaries功能
    //即在渲染过程中报错了,可以选择新的渲染状态(提示有错误的状态),来更新页面
    //默认是0即更新
    tag: UpdateState, //0更新 1替换 2强制更新 3捕获性的更新
  };
}

因此要改成ForceUpdate,以便Reac进行Update优先级排序

三、综上 执行setStateforUpdateReact进行更新的流程为: (1)获取this上的fiber对象 (2)计算currentTime (3)根据(1)fiber(2)currentTime计算fiber对象的expirationTime (4)根据(3)expirationTime创建update对象 (5)将setState中要更新的对象赋值到(4)update.payload (6)将setState中要执行的callback赋值到(4)update.callback (7)update入队updateQueue (8)进行任务调度


本文分享自微信公众号 - webchen(webchen1995)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏fanzhh的技术笔记

使用Sqlite3+Express.js+React实现在线答题(下)

在使用Sqlite3+Express.js+React实现在线答题(上)中,我们将题目数据从word文件转为txt格式并导入到sqlite3中,使用Expres...

11820
来自专栏fanzhh的技术笔记

Django REST + React + Redux 上传文件

9520
来自专栏跨平台全栈俱乐部

React V16.9来了 无痛感升级 加入性能检测 【译-真香】

React 16.9不包含重大更改,旧版本名称在此版本中继续有效。但是,当您使用任何旧名称时,您将看到警告:

33030
来自专栏前端迷

从0实现一个React(一):JSX和虚拟DOM

React是前端最受欢迎的框架之一,解读其源码的文章非常多,但是我想从另一个角度去解读React:从零开始实现一个React,从API层面实现React的大部分...

11530
来自专栏fanzhh的技术笔记

以 React 的方式思考

React 很棒的一点是创建应用中引导你思考的过程。这篇文档中,我们将通过运用React创建一个产品搜索列表,来引导你熟悉这个思考过程。

6930
来自专栏老九学堂

一个9年开发经验程序员的分享,从零基础到专业工程师?

一次规划局的项目,规划局的职员很是钦佩地说:“你们真了不起,在电脑上敲敲键盘就能做出软件来。”

12230
来自专栏程序员成长充电站

2019年要学习的前5个前端开发主题

TypeScript是2018年最令人惊讶的增长故事之一.npm调查发现,有46%的npm用户使用TypeScript。它现在不仅是使用Angular的默认语言...

19320
来自专栏fanzhh的技术笔记

React人机验证控件

在使用React做前端,用户注册页面因为要短信验证,短信服务商要求加人机验证,于是我找到了 react-captcha-generator。

19020
来自专栏fanzhh的技术笔记

React.js 概念与入门

React 是Facebook开发的UI库,这个库有助于创建交互式、基于状态、可重用的UI部件。React已经应用与Facebook,Instagram全部是用...

9520
来自专栏fanzhh的技术笔记

使用Sqlite3+Express.js+React实现在线答题(上)

将word格式的题库转为txt格式,导入至sqlite3中,使用Express.js做服务端提供json格式数据,使用React做前端获取服务端数据。本文为第一...

12730

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励