Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React inaccurately calculating the height of a div on first page load

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:

enter image description 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

like image 800
umbriel Avatar asked Oct 20 '25 00:10

umbriel


2 Answers

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
  });  
}, []);
like image 86
Sohaib Avatar answered Oct 22 '25 14:10

Sohaib


  1. To make sure all images are loaded, you can use the onLoad event on that HTML element.

  2. handleLoad will set state 'loaded' to true when done.

  3. 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

like image 33
Badal Saibo Avatar answered Oct 22 '25 15:10

Badal Saibo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!