Skip to content

Latest commit

 

History

History

enqueueSetState

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
  enqueueSetState(instance, payload, callback) {
    // debugger
    /*
      执行setState时
      基数次更新时(1, 3, 5, ...)
      instance对应的fiber是current
      执行processUpdateQueue时fiber的updateQueue不会改变
      偶数次更新时(2, 4, 6, ...)
      instance对应的fiber是workInProgress
      执行processUpdateQueue时fiber的updateQueue会被操作
    */

    /*
      <Ding>
        <button></button>
        ...
      </Ding>

    |----------------------------------------------------------------------
    |
    |                   第一次commit前
    |           
    |           Root
    |             ↓
    |         RootFiber#① ←→ WorkInProgress#①
    |             ↓                 ↓
    |           null              Ding#①
    |             ↓                 ↓
    |          ...null             ...
    |
    |
    |
    |                  第一次commit后
    |           
    |                               Root
    |                                 ↓
    |         RootFiber#① ←→ WorkInProgress#①
    |              ↓                 ↓
    |            null              Ding#①
    |              ↓                 ↓
    |          ...null              ...
    |
    |
    |
    |-------------------------------------------------------------------
    |
    |
    |                   第二次commit前
    |           
    |                             Root
    |                               ↓
    |         RootFiber#① ←→ WorkInProgress#①
    |             ↓                 ↓
    |           null              Ding#①
    |             ↓                 ↓
    |          ...null             ...
    |
    |
    |
    |                       
    | 当执行到performWorkOnRoot时会调用createWorkInProgress参数是 WorkInProgress#①
    | createWorkInProgress中检测到WorkInProgress#①有值 是RootFiber#①
    | 于是直接把RootFiber#①拿来用作本次Root节点的WorkInProgress 并且child指向current的child
    | 也就是指向Ding#①
    |
    |                             Root
    |                               ↓
    |                        WorkInProgress#① ←→ RootFiber#①
    |                               ↓                 │ 
    |                             Ding#① ←————————————┙ 
    |                               ↓
    |                              ...
    |
    |
    |
    | 
    | 创建完本次更新的RootFiber的workInProgress后会继续往下调度子节点
    | 然后会发现本次RootFiber没有要更新的 于是执行bailoutOnAlreadyFinishedWork跳过本次更新
    | 在执行跳过更新的函数时 如果发现有子节点需要更新的话 就执行cloneChildFibers克隆子节点
    | 这个克隆子节点的方法中也是调用的createWorkInProgress
    | 此时发现Ding#①没有alternate 于是会新创建一个fiber对象
    |
    |                              Root
    |                               ↓
    |                        WorkInProgress#① ←→ RootFiber#①
    |                               ↓                 ↓
    |                             Ding#①   ←——————→  Ding#②
    |                               ↓                 │
    |                              ...  ←—————————————┙
    |
    |
    |
    |              第二次commit之后
    | 当本次更新commit之后 会让Root的current指向RootFiber#①
    |
    |                                               Root
    |                                                 ↓
    |                        WorkInProgress#① ←→ RootFiber#①
    |                               ↓                 ↓
    |                             Ding#①   ←——————→  Ding#②
    |                               ↓                 ↓
    |                              ...               ...
    |
    |
    |----------------------------------------------------------------------------
    |
    |
    |              第三次更新commit之前
    | 同样从performWorkOnRoot开始调度Root
    | 先对RootFiber#①执行createWorkInProgress 里面发现它有alternate指向WorkInProgress#①
    |
    |                                               Root
    |                                                 ↓
    |                        WorkInProgress#① ←→ RootFiber#①
    |                               ↓                 ↓
    |                             Ding#①   ←——————→  Ding#②
    |                               ↓                 ↓
    |                              ...               ...
    |
    | 
    | 于是让本次的RootFiber的workInProgress继续指向WorkInProgress#①
    | 变成下边这样
    | 此时虽然在createWorkInProgress中会将workInProgress#①的child初始化成current.child(RootFiber#①)
    | 但是Ding#②的alternate仍然指向Ding#①
    |
    |                                               Root
    |                                                 ↓
    |                                           RootFiber#① ←→ WorkInProgress#①
    |                                                 ↓               │
    |                             Ding#①  ←——————→  Ding#②  ←—————————┙
    |                                                 ↓
    |                                                ...
    |
    |
    | 之后继续往下执行 会和第二次一样 发现RootFiber上没有更新
    | 于是执行bailoutOnAlreadyFinishedWork跳过本次RootFiber的更新
    | 但是在bailoutOnAlreadyFinishedWork中会同样会执行cloneChildFibers
    | 这个函数中也会对本次RootFiber的WorkInProgress#①的子节点Ding#②进行createWorkInProgress
    | 然后同样发现Ding#②有个alternate指向Ding#① 于是乎不创建新的fiber而是复用alternate
    |
    |
    |                                               Root
    |                                                 ↓
    |                                           RootFiber#① ←→ WorkInProgress#①
    |                                                 ↓               ↓
    |                                               Ding#②  ←————→  Ding#①
    |                                                 ↓               │
    |                                                ...  ←———————————┙
    |
    |
    |
    |           第三次commit之后
    |
    | 当执行完commit的第二个循环后 dom节点已经都被渲染好了
    | 于是让root.current = finishedWork
    | finishedWork就是本次更新使用的Root的WorkInProgress
    | 也就是WorkInProgress#① 
    |
    |
    |
    |                                                               Root
    |                                                                 ↓
    |                                           RootFiber#① ←→ WorkInProgress#①
    |                                                 ↓               ↓
    |                                               Ding#②  ←————→  Ding#①
    |                                                 ↓               ↓
    |                                                ...             ...
    |
    |
    |-------------------------------------------------------------------
    |
    | 之后的setState更新逻辑就都一样了
    | 也就是说 初次渲染的时候 除了Root会有个RootFiber以及有个对应的workInProgress作为RootFiber的alternate
    | 剩下的节点都是只有workInProgress没有alternate的 也就是没有current
    | 然后当commit完 也就是都把dom渲染到了浏览器上了 就会让root.current指向本次的workInProgress
    |
    | 之后当某个组件第一次执行了setState的时候 上一轮的workInProgress就会作为本次的current
    | 并且由于是该组件第一次执行setState 所以本组件仍然是没有alternate的
    | 不过在执行跳过没有更新的组件或节点的时候 可能会对该组件执行createWorkInProgress
    | 会创建一个本组件本次更新要用到的workInProgress
    | 这样他就有了alternate链接上一次的workInProgress和这次的workInProgress
    | 最后当commit完之后 会再次让root.current指向本次的workInProgress
    |
    | 之后再对该组件执行setState的话 这个组件就有了alternate了
    | 于是createWorkInProgress中会复用这个组件的alterante
    | 
    | 这样就相当于每次在执行setState时 本次和上次的workInProgress都会交换一次
    | 虽然这次会复用上一次的workInProgress来作为本次的workInProgress 但是属性都是本次新的属性
    | 只不过对于对象的引用地址是没变的
    | 所以当获取实例的 _reactInternalFiber 属性时 每次都是可以获取到的
    |
    |
    |
    */
    // 先获取对应的fiber
    let fiber = instance._reactInternalFiber
    let currentTime = requestCurrentTime()
    let expirationTime = computeExpirationForFiber(currentTime, fiber)
    
    /*
      createUpdate 就是这个东西
      return {
        expirationTime: expirationTime, // 更新的优先级
        tag: UpdateState, // 对应四种情况 0更新(update) 1替换(replace) 2强更(force) 3捕获(capture; 就是渲染时候如果出错了就被捕获了)
        payload: null, // setState传进来的参数 也就是新的state
        callback: null,
        next: null, // 下一个update
        nextEffect: null, // 
      };
    */
    let update = createUpdate(expirationTime)
    // 然后把update上挂上payload payload就是setState的第一个参数
    update.payload = payload
    enqueueUpdate(fiber, update)
    scheduleWork(fiber, expirationTime)
  },