原文连接:前端
https://medium.com/the-guild/under-the-hood-of-reacts-hooks-system-eb59638c9dbanode
首先,让咱们进入须要确保hooks在React的做用域调用的机制,由于你如今可能知道若是在没有正确的上下文调用钩子是没有意义的:react

The dispatchergit
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
}
到此为止既然咱们已经看过了这种简单的封装机制,我但愿咱们转到本文的核心 - Hooks。我想向您介绍一个新概念:github

The hooks queueweb
-
它的初始状态在首次渲染时被建立。 -
她的状态能够即时更新。 -
React会在以后的渲染中记住hook的状态 -
React会根据调用顺序为您提供正确的状态 -
React会知道这个hook属于哪一个Fiber。
{
foo: 'foo',
bar: 'bar',
baz: 'baz',
}
{
memoizedState: 'foo',
next: {
memoizedState: 'bar',
next: {
memoizedState: 'bar',
next: null
}
}
}
· baseState - 将给予reducer的状态对象。
· baseUpdate- 最近的建立了最新baseState的调度操做。
· queue - 调度操做的队列,等待进入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} />
)
}
让咱们更具体一点,谈谈各个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 - 它对组件的生命周期及其工做方式产生了重大影响:微信

Effect hooksapp
Effect hooks 的行为略有不一样,而且有一个额外的逻辑层,我接下来会解释。一样,在我深刻了解实现以前,我但愿你能记住effect hooks的属性:
它们是在渲染时建立的,但它们在绘制后运行。
它们将在下一次绘制以前被销毁。
它们按照已经被定义的顺序执行。


执行全部生命周期和ref回调。生命周期做为单独的过程发生,所以整个树中的全部放置,更新和删除都已经被调用。此过程还会触发任何特定渲染的初始effects。
由useEffect() hook 安排的effects - 基于实现也被称为“passive effects” (也许咱们应该在React社区中开始使用这个术语?!)。
destroy- 从create()返回的回调应该在初始渲染以前运行。
inputs - 一组值,用于肯定是否应销毁和从新建立effe
next - 函数组件中定义的下一个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
}
所以,基于咱们刚刚学到的关于effect hooks的内容,咱们实际上能够在外部向某个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} />
)


本文分享自微信公众号 - 前端技术江湖(bigerfe)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。