Получить размеры элемента через getBoundingClientRect в React - PullRequest
2 голосов
/ 27 марта 2020

Я хотел бы узнать размеры элемента 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, а не на прямые изменения. Очевидно, что рисование компонента в браузере еще не закончено, потому что размеры всегда равны 0.

Если я делаю все это вне useEffect, то это работает. В консоли я вижу 2 раза значения с 0 и затем правильные размеры.

С useEffect:

Dimensions with useEffect

Вне использованияEffect:

Dimensions outside useEffect

Однако я хотел бы сохранить размеры в состоянии, и для этого мне нужно использовать useEffect.

Как я могу добиться этого с помощью getBoundingClientRect или есть другой способ сделать это?

РЕШЕНИЕ

Это не было проблемой, чтобы реагировать самостоятельно. Это ошибка в реакции ioni c V5.

Обычно вы можете сделать это следующим образом:

https://reactjs.org/docs/hooks-faq.html#how -can-i-measure-a-dom- узел

Но вот проблема:

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 Ответ

1 голос
/ 27 марта 2020

вы можете использовать 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>
  );
}`
...