useCallback和useMemo
# 概述
在React中,useCallback和useMemo分别用于缓存函数和变量,当其依赖没发生改变时,可以复用旧的函数或变量,一定程度上可以优化函数组件的性能。
# 源码分析
# useCallback
- 挂载(首次渲染)
函数组件在挂载首次渲染时,useCallback调用的方法是mountCallback。
// callback 表示回调函数,deps是依赖数组,下同
function mountCallback(callback, deps) {
// 调用 mountWorkInProgress方法创建新的hook,并将其放到fiber的memoizedState链表上,最后返回新的hook
const hook = mountWorkInProgressHook();
// 判断依赖,若不存在,则为null
const nextDeps = deps === undefined ? null : deps;
// 将callback和deps 存放到fiber.memoizedState中,以便后续复用
hook.memoizedState = [callback, nextDeps];
// 最后返回callback
return callback;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 组件更新时
函数组件在更新时,hook的分发器dispatch会将useCallback指向updateCallback。
其源码实现如下:
function updateCallback(callback, deps) {
// 调用updateWorkInProgressHook读取旧Fiber的对应旧hook,以及复用旧hook,创建新hook并返回
const hook = updateWorkInProgressHook();
// 新的依赖取值
const nextDeps = deps === undefined ? null : deps;
// 从新hook上取hook之前数据
const prevState = hook.memoizedState;
// 若新依赖不等于null
if (nextDeps !== null) {
const prevDeps = prevState[1];
// 则判断新旧依赖是否发生了改变
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 若没变化,则直接返回之前的callback
return prevState[0];
}
}
// 若新依赖不存在,或者新旧依赖不等,则再次将callback和deps依赖存入到fiber中
hook.memoizedState = [callback, nextDeps];
// 返回callback
return callback;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# useMemo
useMemo缓存的是回调函数的返回值,useCallback缓存的是回调函数,因此useMemo不过就是比useCallback的回调函数执行一次,并将其结果存到fiber.memoizedState的链表中,并返回;后续更新调度时,若依赖没发生改变,就读取旧hook中的值;若发生改变,再次执行回调函数,将它的返回值存起来,同时返回即可。
其源码实现如下:
function mountMemo(
nextCreate,
deps,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateMemo(
nextCreate,
deps,
) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 总结
useCallback和useMemo就是将函数和依赖存放在fiber.meomizedState的链表中。在函数组件重新渲染后,若依赖没有变化,就复用之前的旧值。若useRef这个hook的值不与DOM绑定,只是在函数组件内部使用,在函数组件更新后,也是复用旧值。它们的原理类似。
编辑 (opens new window)
上次更新: 2026/04/03, 01:41:21