Search code examples
javascripthtmlreactjsstateref

How can I dynamically determine height of a conditionally rendered component?


I have a component that is conditionally rendered and i don't know the height of it beforehand (and also would like to avoid setting it manually). It should automatically be equal to the size of its contents.

To retrieve this height i use a ref to determine it. Based on this height i then want to perform some operation (for example use a fraction of the height for its padding, margin etc.).

To clarify: The component is also conditionally rendered, so not visible from the start of the app, but instead is being made visible through some state modification (button click etc.).

The problem is that the styling cannot be applied directly on the first state operation. I think this happens because the ref is attached after the first rendering process? Not sure.

How can i solve this?

Here is a demo code to represent what i mean (and here also the corresponding live example in codesandbox):

import React, { useEffect, useState } from "react";

const App = () => {
  const [isClicked, setClick] = useState(false);
  const [dimension, setDimension] = React.useState({ width: 0, height: 0 });
  const ref = React.useRef(null);

  useEffect(() => {
    if (ref.current) {
      setDimension({
        width: ref.current.clientWidth,
        height: ref.current.clientHeight,
      });
    }
  }, [ref.current]);

  return (
    <div>
      <button onClick={() => setClick(!isClicked)}>Click me</button>
      {isClicked && (
        <div className="App">
          <div ref={ref} style={{ marginTop: dimension.height }}>
            <h1>Hello</h1>
            <h2>World</h2>
            <h3>!</h3>
          </div>
        </div>
      )}
    </div>
  );
};

export default App;

Solution

  • You can only achieve this when you are letting the component/div render but hiding it from the user.

    Here's the working code/example:

    
        useEffect(() => {
            if (isClicked) {
              setDimension({
                width: ref.current.clientWidth,
                height: ref.current.clientHeight,
              });
              document.getElementById("testButton").style.visibility = "visible";
            } else {
              document.getElementById("testButton").style.visibility = "hidden";
            }
          }, [isClicked]);
        
          return (
            <div>
              <button onClick={() => setClick(!isClicked)}>Click me</button>
              <div className="App">
                <div
                  id="testButton"
                  ref={ref}
                  style={{ marginTop: dimension.height, visibility: "hidden" }}
                >
                  <h1>Hello</h1>
                  <h2>World</h2>
                  <h3>!</h3>
                </div>
              </div>
            </div>
          );
        };
    
    

    For more info, you can check out my sandbox: Sandbox