useMemo
和 useCallback
是 React Hooks 中用于性能优化的重要工具,它们通过缓存计算结果或函数,避免不必要的重复计算和子组件渲染.
memo
React.memo 为高阶组件。它与 React.PureComponent 非常相似。默认只会props数据做浅层比较,如果需要控制对比过程我们可以将比较函数作为第二个参数传入:React.memo(MyComp, areEqual)深层比较,函数组件一般优化都是结合memo、useMemo、useCallback性能优化
useMemo
:缓存计算结果
useMemo 用于优化组件的渲染性能。它会在依赖项变化时重新计算 memoized 值,并且只在依赖项变化时重新渲染组件。你应该在以下情况使用 useMemo:
1.昂贵的计算:如果你有一个复杂的计算,它应该只在某些依赖项变化时重新运行,那么 useMemo 非常有用。它会记住最后计算的值,并仅在依赖项变化时重新计算。
2避免不必要的渲染:如果你有一个组件,它在重新渲染时执行昂贵的 DOM 操作,那么你应该通过 useMemo 来优化它,使其只在依赖项变化时重新渲染。
3.依赖项变化时才重新计算值:如果你想基于 props 的某些值来计算一些数据,并且你只想在依赖 props 值变化时重新计算该数据。
//复杂计算
const memoizedValue = useMemo(() => {
return computeExpensiveValue(data); // 仅在 data 变化时重新计算
}, [data]); // 依赖项数组
//避免对象/数组重新创建
const config = useMemo(() => ({
mode: 'dark',
timeout: 1000,
}), []); // 空依赖项表示仅初始化一次
useCallback
:缓存函数
缓存函数本身,避免函数因父组件重渲染而重新创建,导致子组件不必要的更新。
-
传递回调函数给子组件(搭配
React.memo
使用) -
依赖函数引用的副作用(如
useEffect
依赖函数) -
高频事件处理函数(如滚动、拖拽)
-
const memoizedCallback = useCallback(() => { doSomething(a, b); // 仅在 a 或 b 变化时重新创建函数 }, [a, b]); // 依赖项数组 useEffect(() => { fetchData(); }, [fetchData]); // 依赖 useCallback 缓存的 fetchData
const Child = React.memo(({ onClick }) => { /* ... */ }); const Parent = () => { const handleClick = useCallback(() => { console.log('Clicked!'); }, []); // 空依赖项表示函数永不变化 return <Child onClick={handleClick} />; };
注意事项
-
避免过度优化:
只在真正需要时使用(如性能瓶颈),滥用可能增加内存开销。 -
正确处理依赖项:
依赖项缺失会导致缓存数据过期(Stale Closure 问题) -
与
React.memo
配合
// 仅当 props 变化时重渲染
const Child = React.memo(({ data, onClick }) => { /* ... */ });
4.避免缓存简单计算:
// ❌ 不必要的 useMemo
const sum = useMemo(() => a + b, [a, b]);
// ✅ 直接计算即可
const sum = a + b;
常见误区
//用 useMemo 代替 useEffect
// ❌ 错误:useMemo 用于副作用
useMemo(() => {
fetchData(); // 副作用不应放在 useMemo
}, [data]);
// ✅ 正确:使用 useEffect
useEffect(() => {
fetchData();
}, [data]);
//忽略依赖项导致缓存失效
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(count + 1); // ❌ 闭包陷阱:count 始终为初始值
}, []); // 缺少 count 依赖
// ✅ 正确写法:依赖项包含 count
const increment = useCallback(() => {
setCount(c => c + 1); // 或使用函数式更新
}, []);
总结
-
按需缓存:仅在性能敏感场景使用(如大数据处理、高频交互)。
-
配合
React.memo
:避免子组件因父组件无关状态更新而重渲染。 -
优先用函数式更新:解决闭包陷阱(如
setCount(c => c + 1)
)。 -
合理使用 useMemo
和 useCallback
可以显著提升 React 应用性能,但需结合具体场景权衡利弊