hook:useMemo
- 用来记忆并缓存
值,在指定依赖没有更改时,直接返回缓存值,无需重新计算,可用于优化性能
场景代码示例
-
【场景 1】每次渲染都重新计算(不建议使用):
-
每次组件更新后,都会重新计算值,失去了 useMemo 的意义
// 场景:计算属性 - 每次渲染都计算(无意义) const memoizedValue = useMemo(() => computeExpensiveValue(a, b)); // 这里没有依赖项 -
这样写等同于直接调用
computeExpensiveValue(a, b),没有缓存效果 -
⚠️ 不建议使用,useMemo 的核心价值就是缓存,没有依赖项就失去了意义
-
-
【场景 2】依赖变化时重新计算(常用):
-
依赖项变化时才重新计算,否则返回缓存值
// 场景:昂贵计算 - 仅 a 或 b 变化时重新计算 const memoizedValue = useMemo(() => { return computeExpensiveValue(a, b); }, [a, b]); // 依赖 a 和 b -
只有当
a或b发生变化时,才会重新执行计算函数 -
其他状态变化导致组件重新渲染时,直接返回缓存的
memoizedValue
-
-
【场景 3】仅首次渲染计算(少见):
-
只在首次渲染时计算一次,后续永远返回缓存值
// 场景:初始化配置 - 只计算一次 const config = useMemo(() => { return generateConfig(); }, []); // 空依赖数组 -
适用于一次性计算的场景,比如生成初始配置
-
注意:如果真的不需要响应任何变化,直接在组件外定义常量可能更好
-
-
【场景 4】缓存对象/数组引用(常用):
-
避免每次渲染都创建新的对象/数组,导致子组件不必要的重渲染
// 场景:缓存对象引用 - 避免子组件无意义渲染 const user = useMemo(() => { return { name, age }; }, [name, age]); // 只有 name 或 age 变化时才创建新对象 return <UserProfile user={user} />; // user 引用稳定,子组件不会频繁渲染 -
如果不用 useMemo,每次渲染都会创建新的
{ name, age }对象 -
即使 name 和 age 值没变,新对象的引用也不同,导致子组件重新渲染
-
-
【场景 5】缓存 JSX 元素(常用):
-
防止子组件不必要的重新渲染
const [count, setCount] = useState(0); const [time, setTime] = useState(0); // child1 只在 count 变化时重新创建 const child1 = useMemo(() => <Counter count={count} />, [count]); // child2 只在 time 变化时重新创建 const child2 = useMemo(() => <Time time={time} />, [time]); return ( <div> <button onClick={() => setCount(count + 1)}>count + 1</button> <button onClick={() => setTime(time + 1)}>time + 1</button> {child1} {/* count 变化时才重新渲染 */} {child2} {/* time 变化时才重新渲染 */} </div> ); -
点击 count 按钮时,
child2不会重新渲染 -
点击 time 按钮时,
child1不会重新渲染 -
实现了类似
shouldComponentUpdate的效果
-
依赖数组的区别
| 场景 | 依赖数组 | 计算时机 | 使用场景 |
|---|---|---|---|
| 场景 1 | 无 | 每次渲染都计算 | ⚠️ 不建议 |
| 场景 2 | [dep] | 首次 或 依赖变化 | 常用(计算属性) |
| 场景 3 | [] | 仅首次渲染 | 少见(一次性计算) |
| 场景 4 | [dep] | 首次 或 依赖变化 | 常用(缓存对象引用) |
| 场景 5 | [dep] | 首次 或 依赖变化 | 常用(缓存 JSX) |
注意事项
- useMemo 是性能优化手段,不要过度使用,简单计算直接写就好
- useMemo 的计算函数应该是纯函数,不应该有副作用
- 不要依赖 useMemo 来阻止渲染,React 不保证缓存永久有效