I'm looking for a solid strategy to calculate the height of a parent with many children inside, whether the children are images, videos, or fonts or etc. (Think portfolio content)
A simple example of the issue can be found here:
https://codesandbox.io/s/wrong-height-tkvcm?file=/src/useHeight.js:347-545
Images are slow to load so the calculation function gets fired prematurely. And the resulting height calculation is completely off.
Things I've tried
Calculating the ratio of all images setting the image height explicitly. This works but then then I still have videos, fonts, slow APIs that need to be loaded too. So this strategy feels very whack-a-mole.
ResizeObserver. Is unable to reliably wait for the content to load too. Expensive in performance. And this issue.
React setState/useEffect/array dependencies. Gets me pretty far but ultimately do fail because it's difficult to know which state will fire last to update the height calculation.
I tried React concurrent mode to try to preload the content before the calculation starts. This seems overkill for non-API cases. I only have local images and assets to worry about.
React.useEffect(() => {
updateHeight();
window.addEventListener("resize", updateHeight);
return () => {
window.removeEventListener("resize", updateHeight);
};
}, [updateHeight]);
I do think preloading content with React is the best way to go, but I'm curious to hear if anyone else tried to solve for calculating dimensions on huge containers with lots of content inside?
More complex example: https://codesandbox.io/s/stupefied-wilson-qzb1i?file=/src/components/CaseWrapper/index.js:756-836
Size discrepancy is likely due to the custom fonts you are using. When you refresh the page you can notice the text shift after the fonts load.
Try removing all the @font-face
in base.css
and problem will disappear.
Alternatively, you can wait for document.fonts.ready
callback before you compute the heights.
React.useEffect(() => {
document.fonts.ready.then(function () {
console.log('Fonts currently being used on the page are now loaded.');
// Calculate height now
});
}, []);
To make sure all images are loaded, you can use the onLoad
event on that HTML element.
handleLoad
will set state 'loaded' to true
when done.
Then you can pass the 'loaded'
state to useHeight
and make sure it calculates the element, once it is done loading
// App.js
...
return() (
const [loaded, setLoaded] = useState(false);
const height = useHeight(elementDOM, loaded); // 3.
// 2.
const handleLoad = () => {
setLoaded(true);
};
...
<div ref={elementDOM} className="box" onLoad={handleLoad}> // 1.
..
..
</div>
...
);
...
// useHeight.js
...
function useHeight(elementRef, isLoaded) {
const updateHeight = useCallback(() => {
if (isLoaded && elementRef && elementRef.current) {
const { height } = elementRef.current.getBoundingClientRect();
setHeight(height);
}
}, [isLoaded, elementRef]);
...
return height;
...
}
...
Forked sandbox
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