ReactJS

useCallback

useCallback 解决的问题、使用方式和注意事项。

发布于 2026年5月30日0 views

useCallback 用来缓存函数引用。

React 组件每次重新渲染时,组件内部定义的函数都会重新创建。大多数时候这没问题,但如果这个函数被传给 React.memo 包裹的子组件,或者被放进其他 Hook 的依赖数组里,就可能导致额外渲染或重复执行。

解决什么问题

看这个例子:

import { memo, useState } from "react";

const SaveButton = memo(function SaveButton({
  onSave,
}: {
  onSave: () => void;
}) {
  console.log("SaveButton render");
  return <button onClick={onSave}>保存</button>;
});

export function Page() {
  const [count, setCount] = useState(0);

  function handleSave() {
    console.log("save");
  }

  return (
    <>
      <button onClick={() => setCount((value) => value + 1)}>
        {count}
      </button>
      <SaveButton onSave={handleSave} />
    </>
  );
}

虽然 SaveButton 用了 memo,但每次 Page 重新渲染时,handleSave 都会重新创建。对子组件来说,onSave 是一个新的函数引用,所以 SaveButton 还是会重新渲染。

使用 useCallback

import { memo, useCallback, useState } from "react";

const SaveButton = memo(function SaveButton({
  onSave,
}: {
  onSave: () => void;
}) {
  console.log("SaveButton render");
  return <button onClick={onSave}>保存</button>;
});

export function Page() {
  const [count, setCount] = useState(0);

  const handleSave = useCallback(() => {
    console.log("save");
  }, []);

  return (
    <>
      <button onClick={() => setCount((value) => value + 1)}>
        {count}
      </button>
      <SaveButton onSave={handleSave} />
    </>
  );
}

这样只要依赖数组没有变化,handleSave 就会保持同一个函数引用。

使用方式

基本格式:

const fn = useCallback(() => {
  // 逻辑
}, [依赖项]);

示例:

const handleSearch = useCallback(() => {
  fetchUsers(keyword);
}, [keyword]);

keyword 改变时,handleSearch 会重新创建;如果 keyword 不变,就复用上一次的函数。

适合使用的场景

适合:

  • 函数传给了 React.memo 子组件。
  • 函数作为 useEffectuseMemo 等 Hook 的依赖。
  • 自定义 Hook 返回函数,希望调用方拿到稳定引用。
  • 组件中有比较重的子组件,并且函数 props 导致它频繁重渲染。

不太适合:

  • 普通按钮点击函数,没有传给 memo 子组件。
  • 函数很简单,组件也很轻。
  • 为了“性能优化”给所有函数都包一层 useCallback

依赖数组

useCallback 的依赖数组很重要。

const handleSubmit = useCallback(() => {
  submitForm(form);
}, [form]);

函数内部用到了外部变量,就应该放到依赖数组里。否则函数可能拿到旧值。

错误示例:

const handleSubmit = useCallback(() => {
  submitForm(form);
}, []);

如果 form 会变化,但依赖数组为空,handleSubmit 可能一直使用第一次渲染时的 form

注意事项

useCallback 不是让函数“不执行”,它只是让函数引用保持稳定。

它不会自动提升业务性能。如果组件本身没有重渲染问题,使用 useCallback 反而会增加理解成本。

还要注意:

  • 依赖项必须写对。
  • 不要为了消除 lint 提示随便删依赖。
  • 如果函数依赖很多状态,先看看状态设计是否可以简化。
  • useCallback 常常和 React.memo 搭配使用,单独使用不一定有收益。

和 useMemo 的区别

useCallback 缓存函数。

const handleClick = useCallback(() => {
  console.log("click");
}, []);

useMemo 缓存计算结果。

const total = useMemo(() => {
  return price * count;
}, [price, count]);

可以简单记:

  • useCallback(fn, deps) 缓存函数。
  • useMemo(() => value, deps) 缓存值。

总结

useCallback 解决的是“函数引用每次渲染都变”的问题。它最常见的用途是配合 React.memo,避免函数 props 导致子组件无意义重渲染;使用时重点是写对依赖数组。