React Hook: useCallback 详解
·
React Hook: useCallback 详解
什么是 useCallback?
useCallback 是一个 React Hook,用于记忆化函数,它返回一个记忆化的回调函数版本,只有当其依赖项之一发生变化时才会改变。这有助于通过防止不必要的重新渲染和函数重新创建来优化性能。
基本语法
const memoizedCallback = useCallback(
() => {
// 函数体
},
[dependencies] // 依赖数组
);
为什么使用它?
- 防止不必要的重新渲染:当将函数作为 props 传递给记忆化的组件(
React.memo)时,useCallback确保函数引用保持不变,除非依赖项发生变化。 - 优化昂贵的操作:避免在每次渲染时重新创建函数。
- 稳定的引用:在其他 Hook(如
useEffect、useMemo等)中作为依赖项时很有用。
实际示例:传递给子组件
这是一个 useCallback 真正重要的实际示例:
// 子组件(已记忆化)
const Button = React.memo(({ onClick, label }) => {
console.log('Button rendered');
return <button onClick={onClick}>{label}</button>;
});
// 父组件
function App() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// ❌ 不使用 useCallback:每次 'name' 改变时 Button 都会重新渲染
// const handleClick = () => setCount(count + 1);
// ✅ 使用 useCallback:只有当 count 逻辑改变时 Button 才会重新渲染
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<Button onClick={handleClick} label={`Count: ${count}`} />
</div>
);
}
何时使用 useCallback
✅ 在以下情况使用:
- 将函数传递给记忆化的子组件(
React.memo) - 在其他 Hook(
useEffect、useMemo)中作为依赖项使用的函数 - 创建函数成本较高
- 大型列表中的回调函数
何时不使用
❌ 在以下情况不要使用:
- 不向子组件传递函数的组件中的简单事件处理程序
- 无论如何都会频繁变化的函数
- 过早优化——只有在存在性能问题时才使用
与 useMemo 的比较
useMemo:记忆化值/结果useCallback:记忆化函数
// useMemo - 记忆化结果
const expensiveValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
// useCallback - 记忆化函数本身
const expensiveFunction = useCallback(() => computeExpensiveValue(a, b), [a, b]);
完整示例
这是一个展示 useCallback 实际应用的完整示例:
import { useState, useCallback, memo } from 'react';
// 记忆化的子组件
const ExpensiveChild = memo(({ onIncrement, count }) => {
console.log('ExpensiveChild rendered');
return (
<div>
<p>Count: {count}</p>
<button onClick={onIncrement}>Increment</button>
</div>
);
});
function ParentComponent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// ✅ 记忆化的回调 - 除非依赖项改变,否则不会改变
const handleIncrement = useCallback(() => {
setCount(prev => prev + 1);
}, []); // 空数组 = 函数永远不会改变
return (
<div>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type something..."
/>
<ExpensiveChild onIncrement={handleIncrement} count={count} />
</div>
);
}
在这个示例中,在输入框中输入不会导致 ExpensiveChild 重新渲染,因为 handleIncrement 由于 useCallback 而具有稳定的引用。
关键要点
useCallback记忆化函数,而不是值- 主要在将函数传递给记忆化组件时使用
- 优先使用函数式状态更新以最小化依赖项
- 不要过度使用——只有在有真正性能收益时才使用
- 始终在依赖数组中包含所有依赖项
更多推荐


所有评论(0)