前端小白成长之路 · React 性能优化系列
📍前言:你的组件“记性太差”怎么办?
当你在 React 写着写着,发现每次重新渲染,组件里的函数就“失忆”了,哪怕你根本没改它。
const handleClick = () => console.log('clicked')
你以为它每次都是“老朋友”,实际上它每次都是“新对象”。如果你把这个函数传给了子组件,那就很可能会引发子组件的无意义更新。
这时候就该 useCallback 出场了,它就像 React 版的“备忘录”,记住你上次的函数,只有依赖变了才重新生成。
💡什么是 useCallback?
官方定义(翻译成人话):
useCallback(fn, deps)会返回一个记住了 deps 的值的函数,只有当 deps 变化时,才会重新返回一个新的函数引用。
通俗讲:
const memoizedCallback = useCallback(() => {
// 逻辑...
}, [依赖]);
相当于:
- 如果依赖不变,我就不重新生成函数。
- 如果依赖变了,才会“更新记忆”。
🎯什么场景该用 useCallback?
✅传函数给子组件 + 子组件做了 React.memo
const Parent = () => {
const [count, setCount] = useState(0);
// 每次渲染都会创建新函数(不加 useCallback)
const handleClick = () => {
console.log('clicked');
};
return (
<>
<button onClick={() => setCount(count + 1)}>+1</button>
<Child onClick={handleClick} />
</>
);
};
const Child = React.memo(({ onClick }) => {
console.log('👶Child rendered');
return <button onClick={onClick}>Child Btn</button>;
});
上面例子中,只要 Parent 渲染,Child 就跟着白给(虽然其实没变)。用 useCallback 后,handleClick 就不再每次都是“新脸孔”,Child 就不渲染了。
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
✅传入依赖的 useEffect/useMemo/useCallback 本身
比如你有一个 useEffect 里用了某个回调函数,如果不稳定,useEffect 就会频繁触发。
🧪项目实战示例:TodoList 性能优化
const TodoApp = () => {
const [todos, setTodos] = useState([]);
const [text, setText] = useState("");
const addTodo = useCallback(() => {
setTodos((prev) => [...prev, text]);
setText("");
}, [text]);
return (
<>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={addTodo}>Add</button>
<TodoList todos={todos} />
</>
);
};
const TodoList = React.memo(({ todos }) => {
console.log("🧾TodoList rendered");
return (
<ul>
{todos.map((todo, idx) => (
<li key={idx}>{todo}</li>
))}
</ul>
);
});
👆 如果 addTodo 不加 useCallback,就有可能导致 TodoList 多渲染一次。
❌常见误区
❗滥用 useCallback 反而适得其反
记住一句话:
useCallback是“优化”,不是“默认操作”。
每个 useCallback 都会有额外的内存开销(React 要去缓存函数和比较依赖),过度使用可能导致性能下降!
什么时候不该用:
- 函数不传递给子组件
- 没有性能问题
- 函数逻辑很小,创建成本极低
🧠useCallback vs useMemo 对比
| Hook | 目的 | 返回值 | 用法 |
|---|---|---|---|
useCallback(fn, deps) | 记住函数 | 函数 | const cb = useCallback(() => {}, []) |
useMemo(fn, deps) | 记住值 | 任意值 | const value = useMemo(() => compute(), []) |
一句话总结:
想记住“函数” →
useCallback,想记住“值” →useMemo。
📌总结
- ✅
useCallback用于缓存函数引用,减少子组件无意义更新 - ✅ 搭配
React.memo效果更佳 - ✅ 不传子组件时,别瞎用
- ⚠️ 是优化工具,不是性能灵丹妙药
- 🤝
useCallback和useMemo是 React 性能优化的黄金搭档
📢写在最后
如果你已经能熟练运用 useCallback,说明你已经从 React 新手迈入了性能敏感型开发者的阶段。虽然“优化”两个字很性感,但“过度优化”就像给冬天的你穿羽绒服又盖电热毯:多余还烫人。
下一步,不妨继续探索 useMemo、React.memo、useTransition 等进阶优化技术,搞懂什么时候该记、什么时候该放。
🚀关注我,一起从小白进阶成为前端性能工程师!