useState, part 1: useState的初始化

2020年2月22日 ... ☕️ 2 min read

useState是函数式组件引入的一个全局方法,用于替代class组件的state。使用的时候很好奇,没有了生命周期,它是怎么工作的呢?全局存储?或者是在fiber节点上挂了啥黑科技?

还是debug吧。

useState
-dispatcher = resolveDispatcher() // 返回当前dispatcher
dispatcher.useState(initialState)
-mountState(initialState)

mountState是初始化的核心函数,做了两件事:初始化一个hook对象,以及绑定dispatch回调。

初始化hook对象

hook结构

{
    baseState:null
    baseUpdate:null
    memoizedState: 'foo' // 初始值    next:null
    queue:{
        last: null,
        dispatch:dispatchAction,
        lastRenderedReducer: basicStateReducer,
        lastRenderedState: 'foo'  // 初始值    }
}

绑定dispatch回调

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

所以调用useState第二个返回值的时候,会实际触发dispatchAction函数,由于这个函数还bind了fiber节点和queue更新队列,所以获取组件变量的问题也解决了。

经过上面步骤,useState最终把初始值放入了hook,即workInProgressHook这个链表里。链表通过next属性串联起来,以firstWorkInProgressHook作为头节点。

usestate-init Fiber Node和workInProgressHook的关联

Fiber结构中,也有一个memoizedState属性,这个属性就保存了firstWorkInProgressHook。通过这个属性,hook与fiber节点就建立了关联。

关于Dispatcher的说明

带hooks初始化时,会有默认值

ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV

其中

  HooksDispatcherOnMountInDEV = {
    // ...
    useCallback: function (callback, deps) {
      // ...
      return mountCallback(callback, deps);
    },
    useContext: function (context, observedBits) {
      // ...
      return readContext(context, observedBits);
    },
    useEffect: function (create, deps) {
      // ...
      return mountEffect(create, deps);
    },
    // ...
    useLayoutEffect: function (create, deps) {
      currentHookNameInDev = 'useLayoutEffect';
      mountHookTypesDev();
      checkDepsAreArrayDev(deps);
      return mountLayoutEffect(create, deps);
    },
    useMemo: function (create, deps) {
      // ...
    },
    useReducer: function (reducer, initialArg, init) {
      // ...
    },
    useRef: function (initialValue) {
      // ...
      return mountRef(initialValue);
    },
    useState: function (initialState) {
      // ...
      try {
        return mountState(initialState);
      } finally {
        ReactCurrentDispatcher.current = prevDispatcher;
      }
    },
    // ...
  };

后续使用useXxx的方法时,基本都会用到resolveDispatcher来获取一个ReactCurrentDispatcher.current,然后调用定义在上面的函数来处理。

#React#useState

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