React.memo包裹父组件时自定义Hook无法接收更新值的问题
问题原因分析
- React.memo的浅比较逻辑:
MyComponent没有接收外部props,React.memo会判定组件无需重渲染——哪怕ref.current发生变化,ref的更新本身不会触发组件重渲染,所以组件函数不会再次执行。 - Hook参数的传递时机:组件首次执行时,DOM尚未挂载,
ref.current为null,此时调用useMyHook传入的element是null,hook内的useEffect因if(element)判断不成立,不会执行打印逻辑。当DOM挂载后ref.current被赋值为目标元素,但组件没有重渲染,useMyHook不会被重新调用,自然无法接收到更新后的element,也就不会触发hook内的useEffect。 - 移除
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
相关产品推荐
相关产品推荐

