一文带你深入 React Hooks 原理与源码
前言
阅读本文前建议先了解 React 渲染相关流程与基本原理,以下是之前针对源码写的粗略文章:
本文对上述源码原理部分不做太多详细说明。
renderWithHooks 函数
在这篇文章 深入 React 源码 render 阶段的 beginWork 流程 提到,当 React 渲染时,判断当前为函数组件,会执行 updateFunctionComponent
// 函数组件
case FunctionComponent: {
return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes)
}
而 updateFunctionComponent
会调用 renderWithHooks
方法,这个方法就是 Hooks 执行的入口了
renderWithHooks 方法核心就是确定 ReactCurrentDispatcher,执行函数组件函数
function renderWithHooks(
current, // 当前函数组件对应的初始化 fiber
workInProgress, // 当前正在工作的 fiber 对象
Component, // 函数组件本身
props, // 函数组件第一个参数 props
secondArg, // 函数组件其他参数
nextRenderLanes // 下次渲染优先级
) {
renderLanes = nextRenderLanes
// 正在处理的函数组件对应 Fiber
currentlyRenderingFiber = workInProgress
// 置空
workInProgress.memoizedState = null // memoizedState 用于保存 hooks 链表信息
workInProgress.updateQueue = null // 存放副作用存储的链表
workInProgress.lanes = NoLanes
// 首次挂载和更新阶段分配不同的调度器
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate
// 执行函数组件
let children = Component(props, secondArg)
if (didScheduleRenderPhaseUpdateDuringThisPass) {
// 防止太多次数的重新渲染
}
// 还原调度器
ReactCurrentDispatcher.current = ContextOnlyDispatcher
// 重置全局变量
renderLanes = NoLanes
currentlyRenderingFiber = null
currentHook = null
workInProgressHook = null
return children
}
通过上面代码可以总结, renderWithHooks
方法主要工作是:
- 将
workInProgress
赋值给currentlyRenderingFiber
,置空workInProgress
树的memoizedState
和updateQueue
- 判断当前是挂载还是更新阶段,挂载则赋予
ReactCurrentDispatcher.current
值为HooksDispatcherOnMount
,更新阶段则赋予值为HooksDispatcherOnUpdate
- 执行函数组件
Component(props, secondArg)
- 重置全局变量为 null。如
currentHook
、workInProgressHook
,以及ReactCurrentDispatcher.current
重置为 ContextOnlyDispatcher,防止在错误时机使用 Hook
// 首次渲染挂载
const HooksDispatcherOnMount = {
readContext,
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
useDebugValue: mountDebugValue,
useDeferredValue: mountDeferredValue,
useTransition: mountTransition,
useMutableSource: mountMutableSource,
useOpaqueIdentifier: mountOpaqueIdentifier,
}
// 更新
const HooksDispatcherOnUpdate = {
readContext,
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef,
useState: updateState,
useDebugValue: updateDebugValue,
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
useMutableSource: updateMutableSource,
useOpaqueIdentifier: updateOpaqueIdentifier,
}
也就是说,通过给 ReactCurrentDispatcher
赋值不同的变量来调用 hooks 不同阶段的处理函数。比如使用 useState 的时候,首次挂载实际调用的是HooksDispatcherOnMount.useState
,即 mountState
方法;更新时调用的是HooksDispatcherOnUpdate.useState
,即updateState
方法。
function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current
return dispatcher
}
function useState(initialState) {
const dispatcher = resolveDispatcher()
return dispatcher.useState(initialState)
}
ReactCurrentDispatcher
在函数组件使用时有HooksDispatcherOnMount
和HooksDispatcherOnUpdate
两种值,当执行完函数组件,就会被重置为 ContextOnlyDispatcher
。如果开发时调用了这个形态下的 hooks 会抛出错误,用于防止开发者在函数组件外部调用 hooks
export const ContextOnlyDispatcher = {
readContext,
useCallback: throwInvalidHookError,
useContext: throwInvalidHookError,
useEffect: throwInvalidHookError,
useImperativeHandle: throwInvalidHookError,
useLayoutEffect: throwInvalidHookError,
useMemo: throwInvalidHookError,
useReducer: throwInvalidHookError,
useRef: throwInvalidHookError,
useState: throwInvalidHookError,
useDebugValue: throwInvalidHookError,
useDeferredValue: throwInvalidHookError,
useTransition: throwInvalidHookError,
useMutableSource: throwInvalidHookError,
useOpaqueIdentifier: throwInvalidHookError,
}
function throwInvalidHookError() {
invariant(
false,
'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
' one of the following reasons:\n' +
'1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
'2. You might be breaking the Rules of Hooks\n' +
'3. You might have more than one copy of React in the same app\n' +
'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.'
)
}
Hooks 原理
useState
mountState
讲完公共的入口函数,接下来来单独看下每个 Hooks 的源码。上文说到 useState,先来看看挂载时调用的 mountState
。
function mountState(initialState) {
// 创建 hook 对象,添加到 workInProgressHook 链表
const hook = mountWorkInProgressHook()
// useState 第一个参数为函数,则执行函数进行初始化赋值
if (typeof initialState === 'function') {
initialState = initialState()
}
hook.memoizedState = hook.baseState = initialState
const queue = (hook.queue = {
pending: null,
dispatch: null, // 负责更新 state 的函数
lastRenderedReducer: basicStateReducer, // 自带 reducer 默认值
lastRenderedState: initialState,
})
// 负责更新 state
const dispatch = (queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber, queue))
return [hook.memoizedState, dispatch]
}
这里可以知道,hook.memoizedState
的值就是我们外部使用时 state 的值
再来看下 mountWorkInProgressHook
函数,作用是新建一个 hook 对象并添加到 hooks 链表,返回 workInProgressHook
,workInProgressHook
可以按 hook 调用顺序指向最新的 hook
function mountWorkInProgressHook() {
// 创建一个 hook 对象
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
}
// 第一个 Hook
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = hook
} else {
// 链表形式串联
workInProgressHook = workInProgressHook.next = hook
}
return workInProgressHook
}
dispatchAction
函数的作用是触发更新
function dispatchAction(fiber, queue, action) {
const eventTime = requestEventTime()
const lane = requestUpdateLane(fiber)
// 创建一个 update 对象,记录此次更新的信息
const update = {
lane,
action,
eagerReducer: null,
eagerState: null,
next: null,
}
// 待更新队列
const pending = queue.pending
// 第一次更新
if (pending === null) {
update.next = update // 链表为空,则指向自己本身
} else {
update.next = pending.next
pending.next = update
}
queue.pending = update // 指向最新的 update
const alternate = fiber.alternate
// 判断当前 fiber 是否处在渲染阶段
if (fiber === currentlyRenderingFiber || (alternate !== null && alternate === currentlyRenderingFiber)) {
didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true
} else {
// 当前 fiber 没有处在渲染阶段
if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) {
const lastRenderedReducer = queue.lastRenderedReducer
if (lastRenderedReducer !== null) {
try {
const currentState = queue.lastRenderedState
// 调用 lastRenderedReducer 获取最新的 state
const eagerState = lastRenderedReducer(currentState, action)
update.eagerReducer = lastRenderedReducer
update.eagerState = eagerState
// is 方法原理是 Object.is 进行浅比较
if (is(eagerState, currentState)) {
return
}
} catch (error) {
} finally {
}
}
}
// 调度更新
scheduleUpdateOnFiber(fiber, lane, eventTime)
}
}
触发更新都会调用到 scheduleUpdateOnFiber
方法,它是整个更新任务的开始。这个方法之前文章已经过了下源码,具体可见:深入 React 源码 render 阶段的 beginWork 流程 - scheduleUpdateOnFiber
总体做的事情:
- 创建一个
update
对象记录更新信息,加入到待更新的pending
队列 - 判断当前 fiber 是否处于渲染阶段,是则不需要更新当前函数组件;不是则需要执行更新
- 执行更新的操作:获取最新 state,与上一次的 state 做浅比较,如果相等则不需要更新;不相等则往下执行
scheduleUpdateOnFiber
进行调度更新。
Hooks 数据结构,存储 hooks 状态和更新相关的信息:
const hook = {
memoizedState: null, // 存储了特定 hook 的缓存状态。对于不同的 hook 其值会有所不同
baseState: null, // 存储了 hook 的初始状态
baseQueue: null, // 初始队列
queue: null, // 需要更新的队列
next: null, // 下一个 hook
}
updateState
讲完了 useState 的挂载,接下来来看看更新的函数 updateState
function updateState(initialState) {
return updateReducer(basicStateReducer, initialState)
}
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action
}
const HooksDispatcherOnUpdate = {
useReducer: updateReducer,
useState: updateState,
}
我们可以看到 useState 的更新时调用的是 updateReducer
方法,底层复用了 useReducer
的更新方法,所以可以说 useState 其实就是有默认 reducer 参数(basicStateReducer
) 的 useReducer
。
markSkippedUpdateLanes
:React 的调度和渲染过程中,可能会有一些低优先级的更新被跳过,以便更快地处理高优先级的更新。而该函数的主要目的就是将这些被跳过的更新标记起来,以便在后续的渲染过程中重新处理它们
updateReducer
主要工作是处理组件的 state 更新,大致如下:
- 合并并处理所有的更新
- 遍历队列的 update 对象,使用 action 和 reducer 计算出最新的状态,并返回最新的 state 和更新 state 的函数
function updateReducer(reducer, initialArg, init) {
// 获取当前 fiber 对象的 hook 信息,即 workInProgressHook
const hook = updateWorkInProgressHook()
const queue = hook.queue
queue.lastRenderedReducer = reducer
const current = currentHook
const baseQueue = current.baseQueue
const pendingQueue = queue.pending
if (pendingQueue !== null) {
// ... 将 pending queue 合并到 base queue
}
// 处理更新队列
if (baseQueue !== null) {
const first = baseQueue.next
let newState = current.baseState
let newBaseState = null
let newBaseQueueLast = null
let update = first
// 循环计算新状态
do {
const updateLane = update.lane
// updateLane 的优先级不在当前渲染阶段的优先级范围内时
if (!isSubsetOfLanes(renderLanes, updateLane)) {
// ...
markSkippedUpdateLanes(updateLane) // 将被跳过的更新标记起来,以便在后续的渲染过程中重新处理它们
} else {
// ...
if (update.eagerReducer === reducer) {
newState = update.eagerState
} else {
// 计算新状态
const action = update.action
newState = reducer(newState, action) // 执行 reducer,得到最新 states
}
}
update = update.next
} while (update !== null && update !== first)
// ...
if (!is(newState, hook.memoizedState)) {
// 标记在当前渲染周期中接收到了更新
markWorkInProgressReceivedUpdate() // 该函数只是将 didReceiveUpdate 全局变量设置为 true
}
// 更新 memoizedState 的值
hook.memoizedState = newState
hook.baseState = newBaseState
hook.baseQueue = newBaseQueueLast
queue.lastRenderedState = newState
}
// 返回最新的 state 和更新 state 的函数
const dispatch = queue.dispatch
return [hook.memoizedState, dispatch]
}
上述代码只保留一些关键的逻辑,再来看下 updateWorkInProgressHook
的作用:构建 hooks 链表并按顺序复用上一次的 hook 状态信息,确保 currentHook
和workInProgressHook
有正确的指向。
这里要知道一个源码细节帮助理解,就是上面我们提到的,当组件更新的时候,currentHook
和 workInProgressHook
都会被重置为 null
function updateWorkInProgressHook() {
let nextCurrentHook
// 当前 fiber 的第一个 Hook
if (currentHook === null) {
const current = currentlyRenderingFiber.alternate
if (current !== null) {
nextCurrentHook = current.memoizedState
} else {
nextCurrentHook = null
}
} else {
nextCurrentHook = currentHook.next
}
let nextWorkInProgressHook
// 当前 fiber 的第一个 Hook
if (workInProgressHook === null) {
nextWorkInProgressHook = currentlyRenderingFiber.memoizedState
} else {
nextWorkInProgressHook = workInProgressHook.next
}
if (nextWorkInProgressHook !== null) {
workInProgressHook = nextWorkInProgressHook
nextWorkInProgressHook = workInProgressHook.next
currentHook = nextCurrentHook
} else {
currentHook = nextCurrentHook
// 生成新的 hook 对象,复用 currentHook
const newHook = {
memoizedState: currentHook.memoizedState, // 上次渲染时所用的 state
baseState: currentHook.baseState, // 一次更新中,产生的最新 state
baseQueue: currentHook.baseQueue, // 最新的 update 队列
queue: currentHook.queue, // 当前 update 队列
next: null,
}
// 第一个 Hook
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = newHook
} else {
workInProgressHook = workInProgressHook.next = newHook
}
}
return workInProgressHook
}
useReducer
mountReducer
mountReducer
的实现思路与 mountState
大致相同,区别主要在于 mountState
有默认 reducer 参数 basicStateReducer
,而 mountReducer
的 reducer 则需依赖外部传入。
function mountReducer(reducer, initialArg, init) {
const hook = mountWorkInProgressHook() // 创建 Hook
let initialState
if (init !== undefined) {
initialState = init(initialArg) // 处理初始 state 的函数(可选)
} else {
initialState = initialArg // 第二个参数,指定初始值
}
// 初始值赋值给 hook.memoizedState
hook.memoizedState = hook.baseState = initialState
// 创建更新队列,将 reducer 和 初始化后的 state 传入
const queue = (hook.queue = {
pending: null,
dispatch: null,
lastRenderedReducer: reducer,
lastRenderedState: initialState,
})
// 负责更新 state
const dispatch = (queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber, queue))
return [hook.memoizedState, dispatch]
}
updateReducer
同上,useState 部分已经解析了,此处略过。
useEffect
mountEffect
useEffect
挂载时调用的是 mountEffect
方法,该方法是返回 mountEffectImpl
的调用结果。
mountEffectImpl
则是核心函数,它确保组件挂载时,副作用会被正确地执行和管理,主要工作:
- 创建 hook 对象,添加到 workInProgressHook 链表
- 创建 effect 并添加到 当前 fiber 的 updateQueue 的链表上,并将该 effect 赋值给 hook.memoizedState 属性
function useEffect(create, deps) {
const dispatcher = resolveDispatcher()
return dispatcher.useEffect(create, deps)
}
function mountEffect(create, deps) {
return mountEffectImpl(
// UpdateEffect 表示更新 Update;PassiveEffect 表示副作用类型为 useEffect
UpdateEffect | PassiveEffect,
HookPassive,
create, // useEffect 第一个参数,即副作用函数
deps
)
}
function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
// 1. 创建 hook 对象,添加到 workInProgressHook 链表
const hook = mountWorkInProgressHook()
const nextDeps = deps === undefined ? null : deps
// 给当前 fiber 打上 "UpdateEffect | PassiveEffect" 标记
currentlyRenderingFiber.flags |= fiberFlags
// 2. 创建 effect 并添加到 当前 fiber 的 updateQueue 的链表上,最后将该 effect 赋值给 hook.memoizedState 属性
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined, // 首次挂载 destroy 参数传 undefined
nextDeps
)
}
useState
源码的 hook.memoizedState
存的是 state 值,而 useEffect
存的是 pushEffect
函数的返回结果,即 effect 对象(链表)
pushEffect
函数主要工作:创建 effect,并添加到 updateQueue 链表,最后返回 effect
function pushEffect(tag, create, destroy, deps) {
// 1. 创建 effect
const effect = {
tag, // 用于区分 useEffect 和 useLayoutEffect
create,
destroy, // mountEffectImpl 该参数传的是 undefined
deps,
next: null,
}
// 2. 将 effect 添加到 updateQueue 链表
let componentUpdateQueue = currentlyRenderingFiber.updateQueue
// fiber 节点不存在时则初始化 updateQueue
if (componentUpdateQueue === null) {
// 创建新的 updateQueue
componentUpdateQueue = createFunctionComponentUpdateQueue() // 返回 { lastEffect: null }
currentlyRenderingFiber.updateQueue = componentUpdateQueue
componentUpdateQueue.lastEffect = effect.next = effect
} else {
const lastEffect = componentUpdateQueue.lastEffect
if (lastEffect === null) {
componentUpdateQueue.lastEffect = effect.next = effect
} else {
// 构成单向环形链表:lastEffect 为最新的 effect,lastEffect.next 为第一个 effect
const firstEffect = lastEffect.next
lastEffect.next = effect
effect.next = firstEffect
componentUpdateQueue.lastEffect = effect
}
}
return effect
}
updateEffect
更新时调用的核心方法是 updateEffectImpl
,主要工作是:
- 获取当前 fiber 对象的 hook 信息
- 对比新旧依赖项,如果依赖项不变则创建 effect 到链表中(但最终并不会执行, tag 不含 HookHasEffect);不相等则继续执行,最终也会创建 effect 到链表中(tag 含 HookHasEffect)
- 依赖项变化时:给当前 fiber 打上 PassiveEffect,表示存在需要执行的 useEffect;并在创建 effect 时传入
HookHasEffect
,表示有副作用,commit 阶段则会执行 effect 的回调和销毁函数
function updateEffect(create, deps) {
return updateEffectImpl(UpdateEffect | PassiveEffect, HookPassive, create, deps)
}
// 这里传进来的 fiberFlags 值为 UpdateEffect | PassiveEffect(给 fiber 使用的标记)
// 这里传进来的 hookFlags 表示 Passive EffectTag (标识是 useEffect 还是 useLayoutEffect)
function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
const hook = updateWorkInProgressHook()
const nextDeps = deps === undefined ? null : deps
let destroy = undefined
if (currentHook !== null) {
// 上一次的 effect 对象
const prevEffect = currentHook.memoizedState
destroy = prevEffect.destroy
// 有依赖项
if (nextDeps !== null) {
// 上一次的依赖数组
const prevDeps = prevEffect.deps
// 新旧依赖相等
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 相等也把 effect 加入链表,确保顺序一致
pushEffect(hookFlags, create, destroy, nextDeps) // effect tag 少了 HookHasEffect,后续处理看做无更新
return
}
}
}
// 给当前 fiber 打上 "UpdateEffect | PassiveEffect" 标记
currentlyRenderingFiber.flags |= fiberFlags
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags, // HookHasEffect 表示有副作用,这里 hookFlags 传值为 Passive,两者合计表示 useEffect 有副作用
create, // 回调函数
destroy, // 销毁函数
nextDeps // 当前最新的依赖项
)
}
// 用于判断两个依赖数组的值是否相等
function areHookInputsEqual(nextDeps, prevDeps) {
if (prevDeps === null) {
return false
}
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
// Object.is 判断是否相等
if (is(nextDeps[i], prevDeps[i])) {
continue
}
return false
}
return true
}
useLayoutEffect
useEffect 和 useLayoutEffect 底层复用的都是同个函数,只不过第一个和第二个传参不一样,依靠 fiber.flags
和 effect.tag
实现对 effect 的识别。
两者最大的区别是 useEffect
为异步执行,而 useLayoutEffect
为同步执行。
fiber.flags
不同useEffect
:值为UpdateEffect | PassiveEffect
useLayoutEffect
:值为UpdateEffect
effect.tag
不同useEffect
:值为HookHasEffect | HookPassive
useLayoutEffect
:值为HookHasEffect | HookLayout
function mountLayoutEffect(create, deps) {
return mountEffectImpl(UpdateEffect, HookLayout, create, deps)
}
function updateLayoutEffect(create, deps) {
return updateEffectImpl(UpdateEffect, HookLayout, create, deps)
}
useRef
useRef 的实现是最容易读懂的。
mountRef
就是生成一个对象并返回,结构为{ current: 值 }
,current
属性来保存初始化的值updateRef
则直接返回缓存在hook.memoizedState
的 ref 对象
function mountRef(initialValue) {
const hook = mountWorkInProgressHook()
const ref = { current: initialValue } // 创建ref对象
hook.memoizedState = ref // ref 对象赋值给 memoizedState,保存的是内存地址
return ref // 返回 ref 对象,当 current 属性发生变化时,组件不会重新渲染
}
function updateRef(initialValue) {
const hook = updateWorkInProgressHook()
// 直接返回缓存在 hook.memoizedState 的 ref 对象
return hook.memoizedState
}
useCallback
mountCallback
:将 useCallback 的回调函数与依赖项以数组形式保存到hook.memoizedState
updateCallback
:判断新旧依赖项是否相等,相等则取出上一次的hook.memoizedState
的缓存值,返回上一次回调函数的引用;不相等则返回新传入的回调函数
function mountCallback(callback, deps) {
const hook = mountWorkInProgressHook()
const nextDeps = deps === undefined ? null : deps
// memoizedState 缓存 useCallback 的两个参数的引用数组
hook.memoizedState = [callback, nextDeps]
return callback
}
function updateCallback(callback, deps) {
const hook = updateWorkInProgressHook()
// 获取更新时的依赖项
const nextDeps = deps === undefined ? null : deps
// 取出上一次的数组值
const prevState = hook.memoizedState
if (prevState !== null) {
if (nextDeps !== null) {
// 取出第二个参数依赖项数组
const prevDeps = prevState[1]
// 依赖项不变则取出上一次的回调函数
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0] // 返回缓存的函数
}
}
}
// 依赖项发生变化,返回新传入的函数
hook.memoizedState = [callback, nextDeps]
return callback
}
useMemo
mountMemo
:将 useMemo 的缓存计算结果与依赖项以数组形式保存到hook.memoizedState
updateMemo
:判断新旧依赖项是否相等,相等则取出上一次的hook.memoizedState
的缓存值,返回上一次计算结果;不相等则返回执行回调函数,返回回调函数的计算结果。
function mountMemo(nextCreate, deps) {
const hook = mountWorkInProgressHook()
const nextDeps = deps === undefined ? null : deps
const nextValue = nextCreate() // 执行函数返回计算结果
// memoizedState 缓存 useMemo 的两个参数的引用数组
hook.memoizedState = [nextValue, nextDeps]
return nextValue
}
function updateMemo(nextCreate, deps) {
const hook = updateWorkInProgressHook()
// 获取更新时的依赖项
const nextDeps = deps === undefined ? null : deps
const prevState = hook.memoizedState
if (prevState !== null) {
if (nextDeps !== null) {
// 取出第二个参数依赖项数组
const prevDeps = prevState[1]
// 依赖项不变则取出上一次的计算结果
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0] // 返回缓存的结果值
}
}
}
// 依赖项发生变化
const nextValue = nextCreate() // 重新执行函数返回最新计算结果
hook.memoizedState = [nextValue, nextDeps]
return nextValue
}
看上面代码,可以看出 useMemo
和 useCallback
实现思路几乎一致,区别在于 useMemo
需要会将外部传入的函数 nextCreate()
直接执行,这与两者的用法相关。
总结
hooks 实现是基于 fiber 的,通常链表形式串起来。每个 fiber 节点的 memoizedState 保存了对应的数据,不同的 hooks 通过使用该对应数据完成对应的逻辑功能。
useState/useReducer
:memoizedState
等于 state 的值useEffect
:memoizedState
保存的是 effect 链表(包含 useEffect 第一个参数回调与第二个参数依赖项数组的值)useRef
:memoizedState
等于{ current: 当前值 }
useCallback
:保存两个参数值,memoizedState
等于[callback, deps]
,缓存的是函数useMemo
:保存两个参数值,memoizedState
等于[callback(), deps]
,缓存的是函数执行计算结果
参考文章
转载自:https://juejin.cn/post/7239725302015918140