React hooks的原理和實現(xiàn)(一)

前言:Hooks已經出現(xiàn)很久了,它的出現(xiàn)原因在這里就不再贅述,我們可以在官網看到很詳細的解釋,今天來總結下hooks是怎么實現(xiàn)的。

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

我們可以看到初次渲染時執(zhí)行了render方法,在組件內部執(zhí)行了useState函數,在調用setState后又重新走一遍組件的渲染流程,同樣也執(zhí)行了useState方法,這次卻拿到了newState。同樣的函數,同樣的傳參為什么會得到不同的值呢?你肯定會一下子就想到了,當然是因為mountupdate生命周期不同啊,那么我們就看下源碼是怎么實現(xiàn)的吧。

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);
}

如上是useStateuseEffect兩個api最終暴露的源碼,可以看到返回的函數是掛載在dispatcher對象上的,我們再往下看一下就會發(fā)現(xiàn)不一樣了。如下:

export function renderWithHook(workInProgress: FiberNode, render: any) {
  currentlyFiber = workInProgress;
  if (currentlyFiber.memoizedState === null) {
    dispatcher.current = HooksDispatcherOnMountInDEV;
  } else {
    dispatcher.current = HooksDispatcherOnUpdateInDEV;
  }
  return render(); // 里面包含節(jié)點渲染的調度
}

源碼比較長,我這里精簡了很多,可以看到在渲染組件時,判斷Fiber節(jié)點是否存在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階段執(zhí)行的底層函數是不同的,mount階段對應mounXXX函數,update階段對應updateXXX函數。

因此,從代碼的運行和源碼我們可以得到如下結論:

  • hooks函數不同生命周期,對應的底層函數不同;
  • 將不同生命周期執(zhí)行的底層函數都掛載在了dispatcher對象上,我們在使用時不用去處理生命周期的差異,只用專注于我們的業(yè)務邏輯;



這篇文章會分為三章來寫,今天只是簡單的寫個引子,后面請繼續(xù)關注二哈!上面關于hooks鏈表這個名詞,接下來第二章解釋哈~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容