美文网首页
React源码解析之setState和forceUpdate

React源码解析之setState和forceUpdate

作者: 小进进不将就 | 来源:发表于2019-10-02 20:55 被阅读0次

    一、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,以便React进行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)进行任务调度


    (完)

    相关文章

      网友评论

          本文标题:React源码解析之setState和forceUpdate

          本文链接:https://www.haomeiwen.com/subject/orpcectx.html