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
作为头节点。
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
,然后调用定义在上面的函数来处理。