简述
useMemo
和useCallback
相对来说源码比较简单,在函数组件执行到对应的Hook
时,同样会将包含该Hook
信息的对象链接到Fiber节点的memoizedState
属性上的Hooks
链表。
useMemo
的Hook
对象的memoizedState
属性上存的值为计算后的值和依赖数组 —— hook.memoizedState = [nextValue, nextDeps]
。
useCallback
的Hook
对象的memoizedState
属性上存的值为回调函数和依赖数组 —— hook.memoizedState = [callback, nextDeps]
。
以下源码浅析React版本为17.0.1。
useMemo
在React中,Hooks
在Mount时和Update时使用的是两个不同函数(useContext
除外)。
Mount时
function mountMemo<T>( nextCreate: () => T, deps: Array<mixed> | void | null,): T { // 添加到Fiber节点上的Hooks链表 const hook = mountWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; // 计算需要memo的值 const nextValue = nextCreate(); // hook数据对象上存的值 hook.memoizedState = [nextValue, nextDeps]; return nextValue;}
Update时
function updateMemo<T>( nextCreate: () => T, deps: Array<mixed> | void | null,): T { // 找到该useMemo对应的hook数据对象 const hook = updateWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; // 之前存的[nextValue, nextDeps] const prevState = hook.memoizedState; if (prevState !== null) { if (nextDeps !== null) { const prevDeps: Array<mixed> | null = prevState[1]; // 判断依赖是否相等 if (areHookInputsEqual(nextDeps, prevDeps)) { // 相等就返回上次的值 return prevState[0]; } } } // 不相等重新计算 const nextValue = nextCreate(); hook.memoizedState = [nextValue, nextDeps]; return nextValue;}
useCallback
之前有看别人讲useCallback
是useMemo
的语法糖,现在一看,虽然两个方法完全不同,但也基本完全相同了。
Mount时
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T { // 添加到Fiber节点上的Hooks链表 const hook = mountWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; // memoizedState存的值是callback hook.memoizedState = [callback, nextDeps]; return callback;}
Update时
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T { // 找到该useMemo对应的hook数据对象 const hook = updateWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; const prevState = hook.memoizedState; if (prevState !== null) { if (nextDeps !== null) { const prevDeps: Array<mixed> | null = prevState[1]; if (areHookInputsEqual(nextDeps, prevDeps)) { return prevState[0]; } } } hook.memoizedState = [callback, nextDeps]; return callback;}
这俩方法的源码也太短了,这样就水了一文。
其实一直有一个疑问,如果真的要想让一个函数的地址不发生变化,用useRef
来存函数不是更妙吗?