在 React 中通过 getBoundingClientRect 接收元素的尺寸

Posted

技术标签:

【中文标题】在 React 中通过 getBoundingClientRect 接收元素的尺寸【英文标题】:Receive dimensions of element via getBoundingClientRect in React 【发布时间】:2020-03-27 07:14:55 【问题描述】:

我想以可靠的方式找出 DOM 元素的尺寸。

我的考虑是为此使用 getBoundingClientRect。

  const elementRef = useRef<htmlUListElement | null>(null);
  const [dimensions, setDimensions] = useState<DOMRect | undefined>();

  const element: HTMLUListElement | null = elementRef.current;

  /**
   * Update dimensions state.
   */
  const updateDimensions = useCallback(() => 
    if (!element) return;

    setDimensions(element.getBoundingClientRect());
  , [element, setDimensions]);

  /**
   * Effect hook to receive dimensions changes.
   */
  useEffect(() => 
    if (element) 
      updateDimensions();
    
  , [element, updateDimensions]);

这种方法的问题在于 useEffect 仅对 elementRef.current 做出反应,而不对 rect 更改做出反应。显然浏览器中组件的绘制还没有完成,因为尺寸总是0。

如果我在 useEffect 之外做所有事情,那么它会起作用。在控制台中,我看到 0 的 2 倍值,然后是正确的尺寸。

使用useEffect:

useEffect 之外:

但是,我想将尺寸保存在状态中,为此我需要 useEffect。

如何使用 getBoundingClientRect 实现这一点,或者有其他方法吗?

解决方案

这不是自我反应的问题。这是离子 V5 反应中的一个错误。

通常你可以这样做:

https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node

但这里有个问题:

https://github.com/ionic-team/ionic/issues/19770

我的解决方案是使用:https://github.com/que-etc/resize-observer-polyfill

import  RefCallback, useCallback, useState  from 'react';
import ResizeObserver from 'resize-observer-polyfill';


export function useDimensions(): [RefCallback<HTMLElement | null>, DOMRect | undefined] 
  const [dimensions, setDimensions] = useState<DOMRect | undefined>();

  const ref: RefCallback<HTMLElement | null> = useCallback((node: HTMLElement | null) => 
    if (node) 
      setDimensions(node.getBoundingClientRect().toJSON());

      const resizeObserver = new ResizeObserver((entries) => 
        if (entries.length) 
          setDimensions(entries[0].target.getBoundingClientRect().toJSON());
        
      );

      resizeObserver.observe(node);

      return () => 
        resizeObserver.unobserve(node);
        resizeObserver.disconnect();
      ;
    
  , []);

  return [ref, dimensions];

【问题讨论】:

【参考方案1】:

您可以使用 callBackRef 来实现所需的行为:

import React,  useCallback, useState  from "react";

export default function App() 
  const [dimensions, setDimensions] = useState(null);

  const callBackRef = useCallback(domNode => 
    if (domNode) 
      setDimensions(domNode.getBoundingClientRect());
    
  , []);

  return (
    <div>
      <h1 ref=callBackRef>Measure me</h1>
    </div>
  );
`

【讨论】:

通常是的,但我发现这是一个离子 V4 反应错误。我用解决方案更新了我的问题。【参考方案2】:

如果性能是您关心的问题,那么除了使用 ResizeObserver 之外,我还会使用这个库来避免布局垃圾: https://github.com/ZeeCoder/use-resize-observer

请注意,由于以下特殊原因,它不使用 getBoundingClientReact(): https://gist.github.com/paulirish/5d52fb081b3570c81e3a

【讨论】:

以上是关于在 React 中通过 getBoundingClientRect 接收元素的尺寸的主要内容,如果未能解决你的问题,请参考以下文章

我们可以在 react native 中通过蓝牙连接到热敏收据打印机吗

React中通过地图函数传递道具

在 React 中通过 axios 调用 API(使用自签名证书)时出现 CORS 问题

在后台从 React 组件制作 HTML 字符串,如何在另一个 React 组件中通过危险的SetInnerHTML 使用该字符串

如何在没有 lambda 函数的 React Native 中通过单个处理程序处理几个 TextInput?

使用 react 和 EmailJs 在表单中通过 onSubmit 传递第二个参数