javascript - React inaccurately calculating the height of a div on first page load - Stack Overflow

I'm looking for a solid strategy to calculate the height of a parent with many children inside, wh

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 badges
Add a ment  | 

3 Answers 3

Reset to default 5

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 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
  });  
}, []);
  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

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条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信