【React】709- React Hook 的底層實現(xiàn)原理

原文鏈接:
https://medium.com/the-guild/under-the-hood-of-reacts-hooks-system-eb59638c9dba

? ? ? ? 首先,讓我們進入需要確保hooks在React的作用域調(diào)用的機制,因為你現(xiàn)在可能知道如果在沒有正確的上下文調(diào)用鉤子是沒有意義的:

The dispatcher
let?currentDispatcher
const?dispatcherWithoutHooks?=?{?/*?...?*/?}
const?dispatcherWithHooks?=?{?/*?...?*/?}
function?resolveDispatcher()?{
??if?(currentDispatcher)?return?currentDispatcher
??throw?Error("Hooks?can't?be?called")
}
function?useXXX(...args)?{
??const?dispatcher?=?resolveDispatcher()
??return?dispatcher.useXXX(...args)
}
function?renderRoot()?{
??currentDispatcher?=?enableHooks???dispatcherWithHooks?:?dispatcherWithoutHooks
??performWork()
??currentDispatcher?=?null
}
? ? ? ?到此為止既然我們已經(jīng)看過了這種簡單的封裝機制,我希望我們轉(zhuǎn)到本文的核心 - Hooks。我想向您介紹一個新概念:

The hooks queue
它的初始狀態(tài)在首次渲染時被創(chuàng)建。 她的狀態(tài)可以即時更新。 React會在之后的渲染中記住hook的狀態(tài) React會根據(jù)調(diào)用順序為您提供正確的狀態(tài) React會知道這個hook屬于哪個Fiber。
{
??foo:?'foo',
??bar:?'bar',
??baz:?'baz',
}
{
??memoizedState:?'foo',
??next:?{
????memoizedState:?'bar',
????next:?{
??????memoizedState:?'bar',
??????next:?null
????}
??}
}
· baseState - 將給予reducer的狀態(tài)對象。
· baseUpdate- 最近的創(chuàng)建了最新baseState的調(diào)度操作。
· queue - 調(diào)度操作的隊列,等待進入reducer。
let?currentlyRenderingFiber
let?workInProgressQueue
let?currentHook
//?Source:?https://github.com/facebook/react/tree/5f06576f51ece88d846d01abd2ddd575827c6127/react-reconciler/src/ReactFiberHooks.js:123
function?prepareHooks(recentFiber)?{
??currentlyRenderingFiber?=?workInProgressFiber
??currentHook?=?recentFiber.memoizedState
}
//?Source:?https://github.com/facebook/react/tree/5f06576f51ece88d846d01abd2ddd575827c6127/react-reconciler/src/ReactFiberHooks.js:148
function?finishHooks()?{
??currentlyRenderingFiber.memoizedState?=?workInProgressHook
??currentlyRenderingFiber?=?null
??workInProgressHook?=?null
??currentHook?=?null
}
//?Source:?https://github.com/facebook/react/tree/5f06576f51ece88d846d01abd2ddd575827c6127/react-reconciler/src/ReactFiberHooks.js:115
function?resolveCurrentlyRenderingFiber()?{
??if?(currentlyRenderingFiber)?return?currentlyRenderingFiber
??throw?Error("Hooks?can't?be?called")
}
//?Source:?https://github.com/facebook/react/tree/5f06576f51ece88d846d01abd2ddd575827c6127/react-reconciler/src/ReactFiberHooks.js:267
function?createWorkInProgressHook()?{
??workInProgressHook?=?currentHook???cloneHook(currentHook)?:?createNewHook()
??currentHook?=?currentHook.next
??workInProgressHook
}
function?useXXX()?{
??const?fiber?=?resolveCurrentlyRenderingFiber()
??const?hook?=?createWorkInProgressHook()
??//?...
}
function?updateFunctionComponent(recentFiber,?workInProgressFiber,?Component,?props)?{
??prepareHooks(recentFiber,?workInProgressFiber)
??Component(props)
??finishHooks()
}
const?ChildComponent?=?()?=>?{
??useState('foo')
??useState('bar')
??useState('baz')
??return?null
}
const?ParentComponent?=?()?=>?{
??const?childFiberRef?=?useRef()
??useEffect(()?=>?{
????let?hookNode?=?childFiberRef.current.memoizedState
????assert(hookNode.memoizedState,?'foo')
????hookNode?=?hooksNode.next
????assert(hookNode.memoizedState,?'bar')
????hookNode?=?hooksNode.next
????assert(hookNode.memoizedState,?'baz')
??})
??return?(
????<ChildComponent?ref={childFiberRef}?/>
??)
}
? ? 讓我們更具體一點,談?wù)劯鱾€hooks,從最常見的state hook開始:

State hooks
function?basicStateReducer(state,?action)?{
??return?typeof?action?===?'function'???action(state)?:?action;
}
const?ParentComponent?=?()?=>?{
??const?[name,?setName]?=?useState()
??
??return?(
????<ChildComponent?toUpperCase={setName}?/>
??)
}
const?ChildComponent?=?(props)?=>?{
??useEffect(()?=>?{
????props.toUpperCase((state)?=>?state.toUpperCase())
??},?[true])
??
??return?null
}
? ? 最后,effect hooks? - 它對組件的生命周期及其工作方式產(chǎn)生了重大影響:

Effect hooks
? ? Effect hooks 的行為略有不同,并且有一個額外的邏輯層,我接下來會解釋。同樣,在我深入了解實現(xiàn)之前,我希望你能記住effect hooks的屬性:
它們是在渲染時創(chuàng)建的,但它們在繪制后運行。
它們將在下一次繪制之前被銷毀。
它們按照已經(jīng)被定義的順序執(zhí)行。


執(zhí)行所有生命周期和ref回調(diào)。生命周期作為單獨的過程發(fā)生,因此整個樹中的所有放置,更新和刪除都已經(jīng)被調(diào)用。此過程還會觸發(fā)任何特定渲染的初始effects。
由useEffect() hook 安排的effects - 基于實現(xiàn)也被稱為“passive effects” (也許我們應(yīng)該在React社區(qū)中開始使用這個術(shù)語?!)。
destroy- 從create()返回的回調(diào)應(yīng)該在初始渲染之前運行。
inputs - 一組值,用于確定是否應(yīng)銷毀和重新創(chuàng)建effe
?next - 函數(shù)組件中定義的下一個effect的引用。
const?NoEffect?=?/*?????????????*/?0b00000000;
const?UnmountSnapshot?=?/*??????*/?0b00000010;
const?UnmountMutation?=?/*??????*/?0b00000100;
const?MountMutation?=?/*????????*/?0b00001000;
const?UnmountLayout?=?/*????????*/?0b00010000;
const?MountLayout?=?/*??????????*/?0b00100000;
const?MountPassive?=?/*?????????*/?0b01000000;
const?UnmountPassive?=?/*???????*/?0b10000000;
Default?effect?—?UnmountPassive?|?MountPassive.
Mutation?effect?—?UnmountSnapshot?|?MountMutation.
Layout?effect?—?UnmountMutation?|?MountLayout.
if?((effect.tag?&?unmountTag)?!==?NoHookEffect)?{
??//?Unmount
}
if?((effect.tag?&?mountTag)?!==?NoHookEffect)?{
??//?Mount
}因此,基于我們剛剛學到的關(guān)于effect hooks的內(nèi)容,我們實際上可以在外部向某個fiber注入effect:
function?injectEffect(fiber)?{
??const?lastEffect?=?fiber.updateQueue.lastEffect
??const?destroyEffect?=?()?=>?{
????console.log('on?destroy')
??}
??const?createEffect?=?()?=>?{
????console.log('on?create')
????return?destroy
??}
??const?injectedEffect?=?{
????tag:?0b11000000,
????next:?lastEffect.next,
????create:?createEffect,
????destroy:?destroyEffect,
????inputs:?[createEffect],
??}
??lastEffect.next?=?injectedEffect
}
const?ParentComponent?=?(
??<ChildComponent?ref={injectEffect}?/>
)

回復(fù)“加群”與大佬們一起交流學習~
點擊“閱讀原文”查看70+篇原創(chuàng)文章
