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:
=/src/useHeight.js:347-545
Images are slow to load so the calculation function gets fired prematurely. And the resulting height calculation is pletely 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 plex example: =/src/ponents/CaseWrapper/index.js:756-836
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 pletely 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 plex example: https://codesandbox.io/s/stupefied-wilson-qzb1i?file=/src/ponents/CaseWrapper/index.js:756-836
Share Improve this question edited Jun 30, 2021 at 20:37 umbriel asked Apr 27, 2021 at 1:48 umbrielumbriel 7511 gold badge7 silver badges25 bronze badges3 Answers
Reset to default 5Size 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 pute 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' totrue
when done.Then you can pass the
'loaded'
state touseHeight
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
I'm a bit late to the party, but here is a solution for anyone who runs into a similar issue. Going off the useHeight function, instead of using onLoad, which only calcuates if the main HTML has loaded and not things like images or fonts, we can add:
window.addEventListener("resize", updateHeight);
Then we don't need to use the onLoad function the target container, and we can get rid of the isLoaded property of useHeight all-together.
The final code would look like:
import { useCallback, useEffect, useState } from 'react';
const useHeight = (elementRef) => {
const [height, setHeight] = useState(null);
const updateHeight = useCallback(() => { // get height of target element
if (elementRef && elementRef.current) {
const { height } = elementRef.current.getBoundingClientRect();
setHeight(height);
}
}, [elementRef]);
useEffect(() => {
updateHeight();
// Event listeners:
window.addEventListener("resize", updateHeight); // adjust height on resize
window.addEventListener("load", updateHeight); // ensure page fully loads content before checking height
// Remove Event Listeners:
return () => {
window.removeEventListener("resize", updateHeight);
window.removeEventListener("load", updateHeight);
};
}, [updateHeight]);
return height;
}
export default useHeight;
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745247917a4618499.html
评论列表(0条)