在 React 中通过 getBoundingClientRect 接收元素的尺寸
Receive dimensions of element via getBoundingClientRect in React
我想以可靠的方式找出 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。
如何使用 getBoundingClientRect 实现此目的或是否有其他方法可以做到这一点?
解决方案
不是自己反应的问题。这是 ionic V5 react 中的错误。
通常你可以这样做:
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];
}
您可以使用 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>
);
}`
如果您关心性能,那么除了使用 ResizeObserver 之外,我还会使用这个库来避免布局垃圾:
https://github.com/ZeeCoder/use-resize-observer
请注意,出于这个特殊原因,它不使用 getBoundingClientReact():
https://gist.github.com/paulirish/5d52fb081b3570c81e3a
我想以可靠的方式找出 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。
如何使用 getBoundingClientRect 实现此目的或是否有其他方法可以做到这一点?
解决方案
不是自己反应的问题。这是 ionic V5 react 中的错误。
通常你可以这样做:
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];
}
您可以使用 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>
);
}`
如果您关心性能,那么除了使用 ResizeObserver 之外,我还会使用这个库来避免布局垃圾: https://github.com/ZeeCoder/use-resize-observer
请注意,出于这个特殊原因,它不使用 getBoundingClientReact(): https://gist.github.com/paulirish/5d52fb081b3570c81e3a