I would like to find out the dimensions of a DOM element in a reliable way.
My consideration was to use getBoundingClientRect for this.
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]);
The problem with this approach is that useEffect only reacts to elementRef.current and not to the rect changes. Apparently the drawing of the component in the browser is not finished yet, because the dimensions are always 0.
If I do the whole thing outside the useEffect then it works. In the console I see 2 times values with 0 and then the correct dimensions.
With useEffect:
Outside of useEffect:
However, I would like to save the dimensions in the state and for this I need the useEffect.
How can I achieve this with getBoundingClientRect or is there another way to do this?
SOLUTION
It was not a problem of react it self. It is a bug in ionic V5 react.
Normally you can do it in this way:
https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
But here is an issue:
https://github.com/ionic-team/ionic/issues/19770
My solution is to use: 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];
}
you can use a callBackRef to achieve desired behaviour:
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>
);
}`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With