sansSpoon
sansSpoon

Reputation: 2185

Measure React DOM node on resize with hooks

I'm trying to measure a React DOM node on the window resize event. I've used the example on the React hooks-faq, but it only happens for the first render. If I add a useEffect to listen for the resize, the callback doesn't get called?

function MeasureExample() {
    const [height, setHeight] = useState(0);

    const measuredRef = useCallback(node => {
        if (node !== null) {
            setHeight(node.getBoundingClientRect().height);
        }
    }, []);


    // Adding this effect doesn't re-calculate the height???
    useEffect(() => {

        window.addEventListener("resize", measuredRef);

        return (): void => {
            window.removeEventListener("resize", measuredRef);
        };

    }, [height])

    return (
        <>
            <h1 ref={measuredRef}>Hello, world</h1>
            <h2>The above header is {Math.round(height)}px tall</h2>
        </>
    );
}

Upvotes: 6

Views: 13756

Answers (3)

water
water

Reputation: 333

here is another solution.

function MeasureExample() {
    const [height, setHeight] = useState(0);
    const [node, setNode] = useState(null);

    const measuredRef = useCallback(node => {
        if (node !== null) {
            setNode(node);
        }
    }, []);
    useLayoutEffect(() => {
        if(node){
         const measure = () => {
           setSize(node.getBoundingClientRect().height);
         }
        
         window.addEventListener("resize", measure );

         return () => {
           window.removeEventListener("resize", measure );
         };
       }
    }, [node])

    return (
        <>
            <h1 ref={measuredRef}>Hello, world</h1>
            <h2>The above header is {Math.round(height)}px tall</h2>
        </>
    );
}

Upvotes: 12

Mamunur Rashid
Mamunur Rashid

Reputation: 1185

Measure window screen size and also resize using react hooks

import React from 'react';

function useWindowDimensions() {
  const [width, setWidth] = React.useState(window.innerWidth);
  const [height, setHeight] = React.useState(window.innerHeight);

  React.useState(() => {
    const listener = () => {
      setWidth(window.innerWidth)
      setHeight(window.innerHeight)
    }

    window.addEventListener('resize', listener);

    return () => {
      window.removeEventListener('resize', listener);
    }
  })

  return {
    width,
    height,
  }
}


function App() {
  const { width, height } = useWindowDimensions()
  return (
    <div className="App">
      <h2>width: {width}</h2>
      <h2>height: {height}</h2>
      <p>Resize the window.</p>
    </div>
  )
}

export default App;

Upvotes: 2

Keith
Keith

Reputation: 24241

Personally I would extract out the part of listening for resize events. The advantage is it can be used again for something else.

Because resizing is async, one trick with React is to make the children into a functional return instead of just returning JSX. You can then make your CaptureResize component call the function instead to get it's JSX, and at the same time pass the size to this function.

Below is an example..

const {useLayoutEffect, useRef, useState} = React;

function CaptureResize(props) {
  const {captureRef} = props;
  function updateSize() {
    setSize(captureRef.current.getBoundingClientRect());
  }
  useLayoutEffect(() => {
    updateSize();
    window.addEventListener("resize", updateSize);
    return () => 
      window.removeEventListener("resize", updateSize);
  }, []);
  const [size, setSize] = useState({});
  return props.children(size)
}

function Test() {
  const c = useRef(null);
  return <CaptureResize captureRef={c}>
    {(size) => <React.Fragment>
      <h1 ref={c}>Header 1, Resize window to make this go onto diffrent no. of lines</h1>
      <div>height of header = {size.height}px</div>
    </React.Fragment>}
  </CaptureResize>;
}

ReactDOM.render(<React.Fragment>
  <Test/>
</React.Fragment>, document.querySelector('#mount'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="mount"></div>

Upvotes: 4

Related Questions