react源码解析12.状态更新流程

2022-04-14  本文已影响0人  buchila11

react源码解析12.状态更新流程

视频讲解(高效学习):进入学习

往期文章:

1.开篇介绍和面试题

2.react的设计理念

3.react源码架构

4.源码目录结构和调试

5.jsx&核心api

6.legacy和concurrent模式入口函数

7.Fiber架构

8.render阶段

9.diff算法

10.commit阶段

11.生命周期

12.状态更新流程

13.hooks源码

14.手写hooks

15.scheduler&Lane

16.concurrent模式

17.context

18事件系统

19.手写迷你版react

20.总结&第一章的面试题解答

21.demo

setState&forceUpdate

在react中触发状态更新的几种方式:

我们重点看下重点看下this.setState和this.forceUpdate,hook在第13章讲

  1. this.setState内调用this.updater.enqueueSetState,主要是将update加入updateQueue中

    //ReactBaseClasses.js
    Component.prototype.setState = function (partialState, callback) {
      if (!(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) {
        {
          throw Error( "setState(...): takes an object of state variables to update or a function which returns an object of state variables." );
        }
      }
      this.updater.enqueueSetState(this, partialState, callback, 'setState');
    };
    
    
//ReactFiberClassComponent.old.js
enqueueSetState(inst, payload, callback) {
  const fiber = getInstance(inst);//fiber实例

  const eventTime = requestEventTime();
  const suspenseConfig = requestCurrentSuspenseConfig();
  
  const lane = requestUpdateLane(fiber, suspenseConfig);//优先级

  const update = createUpdate(eventTime, lane, suspenseConfig);//创建update

  update.payload = payload;

  if (callback !== undefined && callback !== null) {  //赋值回调
    update.callback = callback;
  }

  enqueueUpdate(fiber, update);//update加入updateQueue
  scheduleUpdateOnFiber(fiber, lane, eventTime);//调度update
}

enqueueUpdate用来将update加入updateQueue队列

//ReactUpdateQueue.old.js
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    return;
  }

  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;
  const pending = sharedQueue.pending;
  if (pending === null) {
    update.next = update;//与自己形成环状链表
  } else {
    update.next = pending.next;//加入链表的结尾
    pending.next = update;
  }
  sharedQueue.pending = update;
}

[图片上传失败...(image-33b367-1649888806005)]

  1. this.forceUpdate和this.setState一样,只是会让tag赋值ForceUpdate

    //ReactBaseClasses.js
    Component.prototype.forceUpdate = function(callback) {
      this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
    };
    
    
//ReactFiberClassComponent.old.js
enqueueForceUpdate(inst, callback) {
    const fiber = getInstance(inst);
    const eventTime = requestEventTime();
    const suspenseConfig = requestCurrentSuspenseConfig();
    const lane = requestUpdateLane(fiber, suspenseConfig);

    const update = createUpdate(eventTime, lane, suspenseConfig);
    
    //tag赋值ForceUpdate
    update.tag = ForceUpdate;
    
    if (callback !== undefined && callback !== null) {
      update.callback = callback;
    }
    
    enqueueUpdate(fiber, update);
    scheduleUpdateOnFiber(fiber, lane, eventTime);

  },
};
如果标记ForceUpdate,render阶段组件更新会根据checkHasForceUpdateAfterProcessing,和checkShouldComponentUpdate来判断,如果Update的tag是ForceUpdate,则checkHasForceUpdateAfterProcessing为true,当组件是PureComponent时,checkShouldComponentUpdate会浅比较state和props,所以当使用this.forceUpdate一定会更新
//ReactFiberClassComponent.old.js
const shouldUpdate =
  checkHasForceUpdateAfterProcessing() ||
  checkShouldComponentUpdate(
    workInProgress,
    ctor,
    oldProps,
    newProps,
    oldState,
    newState,
    nextContext,
  );

状态更新整体流程

[图片上传失败...(image-bc1d50-1649888806005)]

Update&updateQueue

HostRoot或者ClassComponent触发更新后,会在函数createUpdate中创建update,并在后面的render阶段的beginWork中计算Update。FunctionComponent对应的Update在第11章讲,它和HostRoot或者ClassComponent的Update结构有些不一样

//ReactUpdateQueue.old.js
export function createUpdate(eventTime: number, lane: Lane): Update<*> {//创建update
  const update: Update<*> = {
    eventTime,
    lane,

    tag: UpdateState,
    payload: null,
    callback: null,

    next: null,
  };
  return update;
}

我们主要关注这些参数:

对于HostRoot或者ClassComponent会在mount的时候使用initializeUpdateQueue创建updateQueue,然后将updateQueue挂载到fiber节点上

//ReactUpdateQueue.old.js
export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,
    firstBaseUpdate: null,
    lastBaseUpdate: null,
  shared: {
      pending: null,
    },
    effects: null,
  };
fiber.updateQueue = queue;
}

从触发更新的fiber节点向上遍历到rootFiber

在markUpdateLaneFromFiberToRoot函数中会从触发更新的节点开始向上遍历到rootFiber,遍历的过程会处理节点的优先级(第15章讲)

//ReactFiberWorkLoop.old.js
function markUpdateLaneFromFiberToRoot(
    sourceFiber: Fiber,
    lane: Lane,
  ): FiberRoot | null {
    sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
    let alternate = sourceFiber.alternate;
    if (alternate !== null) {
      alternate.lanes = mergeLanes(alternate.lanes, lane);
    }
    let node = sourceFiber;
    let parent = sourceFiber.return;
    while (parent !== null) {//从触发更新的节点开始向上遍历到rootFiber
      parent.childLanes = mergeLanes(parent.childLanes, lane);//合并childLanes优先级
      alternate = parent.alternate;
      if (alternate !== null) {
        alternate.childLanes = mergeLanes(alternate.childLanes, lane);
      } else {
      }
      node = parent;
      parent = parent.return;
    }
    if (node.tag === HostRoot) {
      const root: FiberRoot = node.stateNode;
      return root;
    } else {
      return null;
    }
  }

例如B节点触发更新,B节点被被标记为normal的update,也就是图中的u1,然后向上遍历到根节点,在根节点上打上一个normal的update,如果此时B节点又触发了一个userBlocking的Update,同样会向上遍历到根节点,在根节点上打上一个userBlocking的update。

如果当前根节点更新的优先级是normal,u1、u2都参与状态的计算,如果当前根节点更新的优先级是userBlocking,则只有u2参与计算

[图片上传失败...(image-48d618-1649888806005)]

调度

在ensureRootIsScheduled中,scheduleCallback会以一个优先级调度render阶段的开始函数performSyncWorkOnRoot或者performConcurrentWorkOnRoot

//ReactFiberWorkLoop.old.js
if (newCallbackPriority === SyncLanePriority) {
  // 任务已经过期,需要同步执行render阶段
  newCallbackNode = scheduleSyncCallback(
    performSyncWorkOnRoot.bind(null, root)
  );
} else {
  // 根据任务优先级异步执行render阶段
  var schedulerPriorityLevel = lanePriorityToSchedulerPriority(
    newCallbackPriority
  );
  newCallbackNode = scheduleCallback(
    schedulerPriorityLevel,
    performConcurrentWorkOnRoot.bind(null, root)
  );
}

状态更新

classComponent状态计算发生在processUpdateQueue函数中,涉及很多链表操作,看图更加直白

带优先级的状态更新

类似git提交,这里的c3意味着高优先级的任务,比如用户出发的事件,数据请求,同步执行的代码等。

[图片上传失败...(image-2ba8a8-1649888806005)]

看demo_8的优先级

现在来看下计算状态的函数

//ReactUpdateQueue.old.js
export function processUpdateQueue<State>(
  workInProgress: Fiber,
  props: any,
  instance: any,
  renderLanes: Lanes,
): void {
  const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
  hasForceUpdate = false;

  let firstBaseUpdate = queue.firstBaseUpdate;//updateQueue的第一个Update
  let lastBaseUpdate = queue.lastBaseUpdate;//updateQueue的最后一个Update
  let pendingQueue = queue.shared.pending;//未计算的pendingQueue

  if (pendingQueue !== null) {
    queue.shared.pending = null;
    const lastPendingUpdate = pendingQueue;//未计算的ppendingQueue的最后一个update
    const firstPendingUpdate = lastPendingUpdate.next;//未计算的pendingQueue的第一个update
    lastPendingUpdate.next = null;//剪开环状链表
    if (lastBaseUpdate === null) {//将pendingQueue加入到updateQueue
      firstBaseUpdate = firstPendingUpdate;
    } else {
      lastBaseUpdate.next = firstPendingUpdate;
    }
    lastBaseUpdate = lastPendingUpdate;

    const current = workInProgress.alternate;//current上做同样的操作
    if (current !== null) {
      const currentQueue: UpdateQueue<State> = (current.updateQueue: any);
      const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
      if (currentLastBaseUpdate !== lastBaseUpdate) {
        if (currentLastBaseUpdate === null) {
          currentQueue.firstBaseUpdate = firstPendingUpdate;
        } else {
          currentLastBaseUpdate.next = firstPendingUpdate;
        }
        currentQueue.lastBaseUpdate = lastPendingUpdate;
      }
    }
  }

  if (firstBaseUpdate !== null) {
    let newState = queue.baseState;

    let newLanes = NoLanes;

    let newBaseState = null;
    let newFirstBaseUpdate = null;
    let newLastBaseUpdate = null;

    let update = firstBaseUpdate;
    do {
      const updateLane = update.lane;
      const updateEventTime = update.eventTime;
      if (!isSubsetOfLanes(renderLanes, updateLane)) {//判断优先级是够足够
        const clone: Update<State> = {//优先级不够 跳过当前update
          eventTime: updateEventTime,
          lane: updateLane,

          tag: update.tag,
          payload: update.payload,
          callback: update.callback,

          next: null,
        };
        if (newLastBaseUpdate === null) {//保存跳过的update
          newFirstBaseUpdate = newLastBaseUpdate = clone;
          newBaseState = newState;
        } else {
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }
        newLanes = mergeLanes(newLanes, updateLane);
      } else {
        //直到newLastBaseUpdate为null才不会计算,防止updateQueue没计算完
        if (newLastBaseUpdate !== null) {
          const clone: Update<State> = {
            eventTime: updateEventTime,
            lane: NoLane,

            tag: update.tag,
            payload: update.payload,
            callback: update.callback,

            next: null,
          };
          newLastBaseUpdate = newLastBaseUpdate.next = clone;
        }

        newState = getStateFromUpdate(//根据updateQueue计算state
          workInProgress,
          queue,
          update,
          newState,
          props,
          instance,
        );
        const callback = update.callback;
        if (callback !== null) {
          workInProgress.flags |= Callback;//Callback flag
          const effects = queue.effects;
          if (effects === null) {
            queue.effects = [update];
          } else {
            effects.push(update);
          }
        }
      }
      update = update.next;//下一个update
      if (update === null) {//重置updateQueue
        pendingQueue = queue.shared.pending;
        if (pendingQueue === null) {
          break;
        } else {
          const lastPendingUpdate = pendingQueue;

          const firstPendingUpdate = ((lastPendingUpdate.next: any): Update<State>);
          lastPendingUpdate.next = null;
          update = firstPendingUpdate;
          queue.lastBaseUpdate = lastPendingUpdate;
          queue.shared.pending = null;
        }
      }
    } while (true);

    if (newLastBaseUpdate === null) {
      newBaseState = newState;
    }

    queue.baseState = ((newBaseState: any): State);//新的state
    queue.firstBaseUpdate = newFirstBaseUpdate;//新的第一个update
    queue.lastBaseUpdate = newLastBaseUpdate;//新的最后一个update

    markSkippedUpdateLanes(newLanes);
    workInProgress.lanes = newLanes;
    workInProgress.memoizedState = newState;
  }

    //...
}
上一篇下一篇

猜你喜欢

热点阅读