hello,这里是潇晨,今天就带着大家一起来手写一个迷你版的hooks
,方便大家理解hook
在源码中的运行机制,配有图解,保姆级的教程,只求同学一个小小的👍,🐶。
第一步:引入React和ReactDOM
因为我们要将jsx
转变为virtual-dom
,这一步分工作就交给babel
吧,而jsx
被babel
进行词法解析之后会形成React.createElement()
的调用,而React.createElement()
执行之后的返回结果就是jsx
对象或者叫virtual-dom
。
又因为我们要将我们的demo渲染到dom
上,所以我们引入ReactDOM
。
import React from "react";
import ReactDOM from "react-dom";
第二步:我们来写一个小demo
我们定义两个状态count
和age
,在点击的时候触发更新,让它们的值加1。
在源码中useState
是保存在一个Dispatcher
对象上面的,并且在mount
和update
的时候取到的是不同的hooks
,所以我们先暂时从Dispatcher
上拿到useState
,等下在来定义Dispatcher
。
接下来定义一个schedule
函数,每次调用的时候会重新渲染组件。
function App() {
let [count, setCount] = Dispatcher.useState(1);
let [age, setAge] = Dispatcher.useState(10);
return (
<>
<p>Clicked {
count} times</p>
<button onClick={
() => setCount(() => count + 1)}> Add count</button>
<p>Age is {
age}</p>
<button onClick={
() => setAge(() => age + 1)}> Add age</button>
</>
);
}
function schedule() {
//每次调用会重新渲染组件
ReactDOM.render(<App />, document.querySelector("#root"));
}
schedule();
第三步:定义Dispatcher
在看这部分前,先来捋清楚fiber
、hook
、update
的关系,看图:
Dispatcher
是什么:Dispatcher
在源码中就是一个对象,上面存放着各种各样的hooks
,在mount
和update
的时候会使用过不同的Dispatcher
,来看看在源码中Dispatcher
是什么样子:
在调用useState
之后,会调用一个resolveDispatcher
的函数,这个函数调用之后会返回一个dispatcher
对象,这个对象上就有useState
等钩子。
那我们来看看这个函数做了啥事情,这个函数比较简单,直接从ReactCurrentDispatcher
对象上拿到current
,然后返回出来的这个current
就是dispatcher
,那这个ReactCurrentDispatcher
又是个啥?别急,继续在源码中来找一下。
在源码中有这样一段代码,如果是在正式环境中,分为两种情况
- 如果满足
current === null || current.memoizedState === null
,说明我们处于首次渲染的时候,也就是mount
的时候,其中current
就是我们fiber
节点,memoizedState
保存了fiber
上hook
,也就是说在应用首次渲染的时候,current fiber
是不存在的,我们还没有创造出任何fiber
节点,或者存在某些fiber
,但是上面没有构建相应的hook
,这个时候就可以认为是处于首次渲染的时候,我们取到的是HooksDispatcherOnMount
- 如果不满足
current === null || current.memoizedState === null
,就说明我们处于更新阶段,也就是update
的时候,我们取到的是HooksDispatcherOnUpdate
if (__DEV__) {
if (current !== null && current.memoizedState !== null) {
ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
} else if (hookTypesDev !== null) {
ReactCurrentDispatcher.current = HooksDispatcherOnMountWithHookTypesInDEV;
} else {
ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
}
} else {
ReactCurrentDispatcher.current =
current === null || current.memoizedState ===