前言:Hooks已经出现很久了,它的出现原因在这里就不再赘述,我们可以在官网看到很详细的解释,今天来总结下hooks是怎么实现的。
hooks api是怎么设计的
这个我们从代码的运行和源码两方面来看下。
同一个函数在挂载和更新阶段为什么结果不一样
function Component() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('hello world!');
}, []);
return (
<div>
<div id="count-text">count = {count}</div>
<div><button id="btn" onClick={setCount(count+1)}>按钮</button></div>
</div>
);
}
上面这段代码,我们都知道count
在初始化时是0
,点击按钮更新后每次都加1
,下面是一个简单的运行流程图:
image.png
我们可以看到初次渲染时执行了render
方法,在组件内部执行了useState
函数,在调用setState
后又重新走一遍组件的渲染流程,同样也执行了useState
方法,这次却拿到了newState
。同样的函数,同样的传参为什么会得到不同的值呢?你肯定会一下子就想到了,当然是因为mount
和update
生命周期不同啊,那么我们就看下源码是怎么实现的吧。
mount阶段和update阶段分别做了什么呢
很容易就可以找到源码,如下:
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
export function useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
如上是useState
和useEffect
两个api最终暴露的源码,可以看到返回的函数是挂载在dispatcher
对象上的,我们再往下看一下就会发现不一样了。如下:
export function renderWithHook(workInProgress: FiberNode, render: any) {
currentlyFiber = workInProgress;
if (currentlyFiber.memoizedState === null) {
dispatcher.current = HooksDispatcherOnMountInDEV;
} else {
dispatcher.current = HooksDispatcherOnUpdateInDEV;
}
return render(); // 里面包含节点渲染的调度
}
源码比较长,我这里精简了很多,可以看到在渲染组件时,判断Fiber节点
是否存在hooks链表
,以此来判断组件目前处于mount
还是update
阶段,从而给dispatcher
对象挂载不同的hooks
函数。函数具体如下:
// hook挂载时的api
const HooksDispatcherOnMountInDEV = {
useState: (initialState) => {
return mountState(initialState);
},
useEffect: (create, deps) => {
return mountEffect(create, deps);
},
}
// hook更新时的api
const HooksDispatcherOnUpdateInDEV = {
useState: (initialState) => {
return updateState(initialState);
},
useEffect: (create, deps) => {
return updateEffect(create, deps);
}
}
从上面可以看出,mount
阶段和update
阶段执行的底层函数是不同的,mount
阶段对应mounXXX
函数,update
阶段对应updateXXX
函数。
因此,从代码的运行和源码我们可以得到如下结论:
- hooks函数不同生命周期,对应的底层函数不同;
- 将不同生命周期执行的
底层函数都挂载在了dispatcher对象上
,我们在使用时不用去处理生命周期的差异,只用专注于我们的业务逻辑;
这篇文章会分为三章来写,今天只是简单的写个引子,后面请继续关注二哈!上面关于
hooks链
表这个名词,接下来第二章解释哈~