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

React.memo包裹父组件时自定义Hook无法接收更新值的问题

问题原因分析
  1. React.memo的浅比较逻辑:MyComponent没有接收外部props,React.memo会判定组件无需重渲染——哪怕ref.current发生变化,ref的更新本身不会触发组件重渲染,所以组件函数不会再次执行。
  2. Hook参数的传递时机:组件首次执行时,DOM尚未挂载,ref.current为null,此时调用useMyHook传入的element是null,hook内的useEffect因if(element)判断不成立,不会执行打印逻辑。当DOM挂载后ref.current被赋值为目标元素,但组件没有重渲染,useMyHook不会被重新调用,自然无法接收到更新后的element,也就不会触发hook内的useEffect。
  3. 移除React.memo后的行为:无memo包裹时,React在严格模式下会触发组件两次渲染,第二次渲染时ref.current已被赋值,useMyHook能拿到有效元素并执行打印;即使非严格模式,组件也可能因父组件的隐式更新重新执行,从而让hook获取到最新的element。
保留React.memo的解决方法

方法一:用状态跟踪ref.current的变化

通过状态更新触发组件重渲染,确保useMyHook能接收到最新的element:

import * as React from "react";

const useMyHook = ({ element }: { element: HTMLElement | null }) => {
  React.useEffect(() => {
    if (element) {
      console.log("element in hook", element);
    }
  }, [element]);
};

const MyComponent = React.memo(() => {
  const ref = React.useRef<HTMLDivElement | null>(null);
  // 用状态跟踪ref.current的变化
  const [element, setElement] = React.useState<HTMLDivElement | null>(null);

  React.useEffect(() => {
    setElement(ref.current);
  }, [ref.current]);

  React.useEffect(() => {
    if (element) {
      console.log("element in parent", element);
    }
  }, [element]);

  useMyHook({ element });

  return <div ref={ref}>my content</div>;
});

export default function App() {
  return <MyComponent />;
}

原理:ref.current变化时,通过useEffect更新状态element,状态变化会触发组件重渲染(React.memo不会阻止内部状态导致的重渲染),此时组件重新执行,useMyHook能拿到最新的element,触发hook内的useEffect。

方法二:让Hook直接接收Ref对象

修改useMyHook,直接传入整个ref对象,在hook内部访问ref.current:

import * as React from "react";

// 修改hook,接收ref对象而非ref.current
const useMyHook = ({ ref }: { ref: React.RefObject<HTMLElement> }) => {
  React.useEffect(() => {
    const element = ref.current;
    if (element) {
      console.log("element in hook", element);
    }
  }, [ref]);
};

const MyComponent = React.memo(() => {
  const ref = React.useRef<HTMLDivElement | null>(null);

  React.useEffect(() => {
    if (ref.current) {
      console.log("element in parent", ref.current);
    }
  }, [ref.current]);

  // 传入整个ref对象
  useMyHook({ ref });

  return <div ref={ref}>my content</div>;
});

export default function App() {
  return <MyComponent />;
}

原理:ref对象本身是稳定的(每次渲染都是同一个引用),hook内的useEffect依赖ref不会频繁触发。在useEffect回调中,DOM已经挂载,ref.current已有值,因此能直接执行打印逻辑。如果需要监听ref.current的后续变化,可以在hook内部添加MutationObserver或相关监听逻辑。

内容的提问来源于stack exchange,提问作者Mike K

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 00:03:28