一、总览:性能优化三件套
useCallback(fn, deps)
:缓存函数,避免每次渲染都新建函数。useMemo(fn, deps)
:缓存值(计算结果),避免重复执行计算。React.memo(Component)
:缓存组件的渲染结果,避免 props 没变却重复渲染。
三者搭配使用,在中大型组件中可显著优化性能,减少重复渲染。
二、逐个详解
(一)useCallback
—— 缓存函数引用
- (1) 每次组件渲染时函数都会重新创建,导致引用变了。
- (2) 使用场景:
- 给子组件传递函数 props 时避免子组件重复渲染。
- 函数作为依赖传入
useEffect
/useMemo
。
- (3) 使用方式:
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
- (4) 注意事项:
- 依赖项变化时函数才会更新。
- 类似
useMemo(() => fn, deps)
。
(二)useMemo
—— 缓存计算结果
- (1) 每次组件渲染时如果存在复杂计算,可能导致性能问题。
- (2) 使用场景:
- 依赖变量变化时才重新计算;
- 缓存对象、数组等引用类型的值。
- (3) 使用方式:
const result = useMemo(() => heavyComputation(data), [data]);
- (4) 注意事项:
- 不要滥用;对性能无益时反而增加复杂性。
- 常用于防止因对象/数组引用变化导致组件重新渲染。
(三)React.memo
—— 缓存组件渲染结果
- (1) 是一个高阶组件,用于函数组件。
- (2) 默认只进行浅层比较,如果 props 没变,则跳过渲染。
- (3) 使用方式:
const MyComponent = React.memo((props) => {
return <div>{props.text}</div>;
});
- (4) 使用场景:
- 子组件 props 不变时,避免不必要的渲染。
- 搭配
useCallback
/useMemo
使用更有效。
- (5) 可选第二参数自定义比较逻辑:
React.memo(Component, (prevProps, nextProps) => {
return prevProps.id === nextProps.id;
});
三、组合使用示例
import React, { useState, useMemo, useCallback } from 'react';
const List = React.memo(({ items, onClickItem }) => {
console.log("List rendered");
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => onClickItem(item)}>
{item.name}
</li>
))}
</ul>
);
});
const App = () => {
const [count, setCount] = useState(0);
const items = useMemo(() => [
{ id: 1, name: "Apple" },
{ id: 2, name: "Banana" },
], []);
const handleClick = useCallback((item) => {
console.log("Clicked:", item.name);
}, []);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count: {count}</button>
<List items={items} onClickItem={handleClick} />
</div>
);
};
分析说明:
List
被React.memo
包裹,只有items
或onClickItem
改变才会重新渲染。items
用useMemo
缓存,防止数组地址变化。handleClick
用useCallback
缓存函数,防止函数引用变化。
四、注意事项与总结
- ✅ 使用这些 Hook 是性能优化手段,不是每个项目都需要用。
- ❌ 滥用会增加代码复杂度,尤其在小组件中意义不大。
- 👀 推荐在以下情况使用:
- 子组件使用了
React.memo
; - 函数或对象在依赖中频繁触发更新;
- 存在大型列表或复杂计算逻辑;
- 性能瓶颈场景下的精细优化。
- 子组件使用了