“记忆力超强”的 useCallback,到底值不值得用?——React 性能优化黑科技详解

85 阅读3分钟

前端小白成长之路 · 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 效果更佳
  • ✅ 不传子组件时,别瞎用
  • ⚠️ 是优化工具,不是性能灵丹妙药
  • 🤝 useCallbackuseMemo 是 React 性能优化的黄金搭档

📢写在最后

如果你已经能熟练运用 useCallback,说明你已经从 React 新手迈入了性能敏感型开发者的阶段。虽然“优化”两个字很性感,但“过度优化”就像给冬天的你穿羽绒服又盖电热毯:多余还烫人。

下一步,不妨继续探索 useMemoReact.memouseTransition 等进阶优化技术,搞懂什么时候该记、什么时候该放。

🚀关注我,一起从小白进阶成为前端性能工程师!