You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用useCallback后子组件仍重渲染?为何需搭配React.memo?

为什么useCallback必须搭配React.memo才能阻止子组件重渲染?

问题描述

我正在研究useCallback和useMemo的工作原理及其对React应用性能的优化作用。为此我创建了一个简单Demo,发现传递给子组件的OnBlurHandler回调会导致父组件重渲染时子组件也随之重渲染。于是我用useCallback包裹该回调以避免子组件重渲染,但出乎意料的是子组件依然会重渲染。直到我用React.memo包裹子组件后,才实现了预期的渲染优化效果。我无法理解为何仅使用useCallback无法阻止子组件重渲染,为何必须搭配React.memo?

示例代码

父组件

// Parent Component
import React, { useState, useEffect, useMemo, useCallback } from "react";
import Input from "./Input";

const UnderStadingHooks = () => {
  console.log("Component Start");     
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);
  const [rdm, setRdm] = useState(0);
  let c;
  useEffect(() => {
    console.log("UseEffect Start");
    c = a + b;
    console.log(a, b, c, d);
  }, [a, b]);

  const d = useMemo(() => {
    console.log("UseMemo Called");
    return a + b;
  }, [a, b]);

  const OnBlurHandler = useCallback(
    (e) => {
      console.log("OnBLur Hablder called");
      if (e.target.name === "a") {
        setA(parseInt(e.target.value));
      } else {
        setB(parseInt(e.target.value));
      }
    },
    [a, b]
  );

  const handleOnClick = () => {
    const ran = Math.ceil(Math.random() * 5);
    setRdm(ran);
  };
  console.log("Component END");
  return (
    <div>
      <h1>Understanding Hooks In Depth</h1>
      <Input
        name="a"
        placeholder="Enter a Value of A"
        OnBlurHandler={OnBlurHandler}
      />
      <Input
        name="b"
        placeholder="Enter a value of B"
        OnBlurHandler={OnBlurHandler}
      />
      <p>
        Values are :{a},{b}
      </p>
      <p>SumEffect:{c}</p>
      <p>SumMemo:{d}</p>
      <button type="button" onClick={handleOnClick}>
        Display Some Random Number
      </button>
      <p>{rdm}</p>
    </div>
  );
};

export { UnderStadingHooks };

子组件

// Child Component
import React, { memo } from "react";

const Input = ({ name, placeholder, id, OnBlurHandler }) => {
  console.log("Input Component Rendered");
  return (
    <input
      id={id}
      name={name}
      placeholder={placeholder}   
      onBlur={OnBlurHandler} 
    />
  );
};

export default memo(Input);

解答

核心逻辑拆解

  • React默认重渲染规则:React的默认行为是,只要父组件触发重渲染,它所有的子组件都会跟着重渲染——不管子组件接收的props有没有变化。这是为了确保UI状态和数据完全同步,避免出现不一致的情况。
  • useCallback的真实作用:useCallback只是帮你缓存函数的引用。当父组件重渲染时,只要useCallback的依赖数组没变化,它就会返回同一个函数对象。但它并没有改变子组件“父重渲染则必重渲染”的默认规则。
  • React.memo的关键作用:React.memo是一个高阶组件,它会给子组件加上props对比逻辑:每次父组件重渲染后,它会浅比较子组件前后两次接收的props,如果props完全没变化,就跳过子组件的重渲染;如果props变了,才会触发子组件更新。

结合你的Demo分析

在你没给子组件加React.memo的时候,就算用useCallback缓存了OnBlurHandler,当父组件通过setRdm更新状态触发重渲染时,子组件依然会跟着重渲染——因为React根本不会去检查子组件的props有没有变化,默认就是父重子必重。

当你给子组件加上React.memo后,它会对比前后传入的所有props:name、placeholder没变化,OnBlurHandler因为被useCallback缓存了,引用也没变化,所以props对比通过,子组件就不会触发重渲染了。

总结

useCallback负责保证props的稳定性,让传递给子组件的回调引用不变;React.memo负责开启props对比机制,让子组件在props不变时跳过重渲染。两者必须配合使用:

  • 没有useCallback,就算有React.memo,每次父组件重渲染都会生成新的回调函数,props对比失败,子组件还是会重渲染;
  • 没有React.memo,就算useCallback保证了props不变,父组件重渲染时子组件依然会无条件跟着重渲染。

内容的提问来源于stack exchange,提问作者Sandeep Garg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 12:26:09