Andrei Rosu
Andrei Rosu

Reputation: 1387

Detect element reference height change

Is it possible to detect when an element reference changes it's height? I tried using the following, but when the height of the element changes for whatever reason, the change is not detected. (Please consider that this must also work in IE11)

useEffect(() => {
  // detect change in reference height
}, [elementRef])

Upvotes: 35

Views: 42727

Answers (5)

John
John

Reputation: 11399

I noticed another answer mentioning to use a ResizeObserver, but the answer is incomplete, so I'd like to add my own.

Using useEffect

You can create a ResizeObserver inside a useEffect-hook, and then do what you want to do when the element changes its size. Remember to disconnect from the resizeObserver in the cleanup function.

useEffect(() => {
  if (!elementRef.current) return;
  const resizeObserver = new ResizeObserver(() => {
    // Do what you want to do when the size of the element changes
  });
  resizeObserver.observe(elementRef.current);
  return () => resizeObserver.disconnect(); // clean up 
}, []);

Using useCallback

As mentioned in the comments, it's also possible to use useCallback instead of useEffect.

You can create a ResizeObserver inside a useCallback-hook, and then do what you want to do when the element changes its size. React has an example on how to measure a DOM node.

const elementRef = useCallback(node => {
  if (!node) return;
  const resizeObserver = new ResizeObserver(() => { 
    // Do what you want to do when the size of the element changes
  });
  resizeObserver.observe(node);
}, []);

Upvotes: 58

rottitime
rottitime

Reputation: 2461

You could just use window.

My example below is optimised with a cleanup removeEventListener function should the component unmount.

const BoxAndHeight = () => {
  const ref = React.useRef(null);
  const [height, setHeight] = React.useState(0);

  const onResize = React.useCallback(() => {
    if (ref.current) setHeight(ref.current.clientHeight);
  }, []);

  React.useEffect(() => {
    window.addEventListener("resize", onResize);
    onResize();
    return () => {
      window.removeEventListener("resize", onResize);
    };
  }, []);

  return (
    <div ref={ref} style={style}>
      <h1>My height: {height}</h1>
      <p>
        {dummyContent}
      </p>
    </div>
  );
}

// Render it
ReactDOM.createRoot(
    document.getElementById("root")
).render(<BoxAndHeight />);

const style = {
  border: "2px solid #ccc",
  padding: "10px",
  backgroundColor: "#eee"
};
const dummyContent = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Distinctio, fugiat. Ex in neque velit perspiciatis recusandae, deleniti illum error ea distinctio obcaecati nisi deserunt ab unde corporis quas magnam quo cupiditate dolor dicta? Eos nostrum delectus suscipit! Hic corrupti, assumenda quo modi rem aperiam voluptas explicabo alias fuga error nulla. Eos tenetur voluptas repellat. Tempore, ab harum. Recusandae impedit adipisci soluta officia sunt quis voluptas, quae ea! Eveniet vero incidunt enim quod, voluptate fugiat maxime deserunt et laudantium quidem, ducimus sunt ratione voluptatem libero neque accusamus praesentium fugit doloremque est nisi excepturi. Quo inventore est soluta culpa quos? Minus, laudantium!";
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

Upvotes: 1

Exifers
Exifers

Reputation: 2822

The most efficient way is to use a resize observer.

You can set it up in a useEffect, and clear it when the component unmounts.

Upvotes: 4

do thuan
do thuan

Reputation: 475

I think you can use elementRef.current.clientHeight in useEffect dependencies in order to listen to tag's height.

I test with this case and it worked.

function App() {
  const tag = useRef();
  const [height, setHeight] = useState(10);
  useEffect(() => {
    console.log("updated", tag?.current?.clientHeight);
  }, [tag?.current?.clientHeight]);

  useEffect(() => {
    setInterval(() => {
      setHeight((height) => height + 10);
      console.log(height, tag.current.clientHeight);
    }, 2000);
  }, []);

  return (
    <div className="App" ref={tag}>
      <div style={{ height: height, backgroundColor: "green" }}></div>
    </div>
  );
}

Here is the codesandbox https://codesandbox.io/embed/reactjs-playground-forked-b8j5n?fontsize=14&hidenavigation=1&theme=dark

Upvotes: -1

noszone
noszone

Reputation: 328

Possible solution is to use a React Refs. Try this example as reference:

export default function Home() {
  const divRef = createRef()
  // ...
  return (
    <div style={{ height: '100vh', width: '100vw' }}>
      <div
        ref={divRef} // ...
      ></div>
    </div>
  )
}

Detailed code here.

Upvotes: -4

Related Questions