useState, part 2: useState更新事件的触发

April 19, 2021 ... ☕️ 2 min read

之前看了useState实际做了dispatch回调的绑定:

dispatch = queue.dispatch = dispatchAction.bind(null, currentFiber, queue)
//...
return [hook.memoizedState, dispatch]

然后通过MemorizedState和Hooks连接起来:

usestate-init Fiber Node和workInProgressHook的关联

这次看一下事件的触发。

比如调用一个set函数

const Foo = () => {
  console.log('123');
  const [data, setData] = useState('foo');
  const clickHandler = () => {
  setData('bar')
  };
  return (<div onClick={clickHandler}>{data}</div>);
};

事件触发部分这里不看,从事件触发的set方法开始。

流程

调用useState第二个返回值的时候,会实际触发dispatchAction函数。所以调用setXXX即调用dispatchAction函数,所以这是整个useState的关键环节。

dispatchAction这个函数bind了currentFiberqueue,所以调用的时候自带3个参数:

fiber, queue, action

其中action可以是一个值,也可以是函数。

dispatchAction
-_lastRenderedReducer // 获取下一个state(由action获取),如果和当前state一致直接退出(这里会使用Object.is对比,相同则直接退出)
// 保存到一个update里,并把update放到更新队列queue里
update{
      expirationTime: _expirationTime,
      suspenseConfig: _suspenseConfig,
      action: action,
      eagerReducer:_lastRenderedReducer,
      eagerState: _eagerState,
      next: linked node
}

多个state存储

如果是多个state,怎么存储呢?

答案是Fiber节点的memoizedState(即firstWorkInProgressHook)里的next属性。

它其实作为一个链表的指针,指向下一个state节点,下一个节点上又能找到memoizedState和next。

这样,所有属于该Fiber的数据,就存储起来了。

只有节点上的memoizedState存储了一个对象,其他(next)都是直接存state内容。

后续更新过程

后面就是正常的触发一个更新过程,附带deadline时间片控制的,不是这里的重点,简单看一下:

scheduleUpdateOnFiber(fiber, expirationTime)
-scheduleCallbackForRoot

-Scheduler_scheduleCallback
queue=[runRootCallback]
performWorkUntilDeadline
-flushSyncCallbackQueue // flush前面的queue队列(依次执行)

函数主体是个循环

do{
  callback = callback();
} while (callback !== null);

-runRootCallback
-renderRoot

这个函数return commitRoot,即callback = commitRoot,继续执行

-commitRoot

总结

整体看来,useState引入的状态变化方法,抛开整体的Fiber结构适配和deadline时间片控制优化,对整体的流程改变其实不大。但是hooks的设计思路(统一通过Dispatcher调度),却能迁移到更多的地方。比如useEffectuseCallback

// useEffect
-dispatcher = resolveDispatcher() // 返回当前dispatcher
dispatcher.useEffect(create, inputs)
-mountEffect(create, inputs)

// useCallback
dispatcher = resolveDispatcher() // 返回当前dispatcher
dispatcher.useCallback(callback, inputs)
-mountCallback(callback, deps)

#React#useState

SideEffect is a blog for front-end web development.
Code by Axiu / rss