最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Measure React DOM node on resize with hooks - Stack Overflow

programmeradmin2浏览0评论

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>
        </>
    );
}

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>
        </>
    );
}
Share Improve this question edited Oct 21, 2019 at 9:28 Joseph D. 12.2k4 gold badges38 silver badges71 bronze badges asked Oct 21, 2019 at 9:14 sansSpoonsansSpoon 2,1853 gold badges26 silver badges45 bronze badges 2
  • it means value of the height state is not changing. console.log(height) ? what you are getting ? is it changing ? – Guruprasad Commented Oct 21, 2019 at 9:21
  • Adding and removing the event listener every time the screen height changes seems a tad wasteful. This is maybe one of those rare occasions passing [] makes sense. Also personally I would split this into 2 components, one component for doing your resize event capture, and then pass the height down as a prop. – Keith Commented Oct 21, 2019 at 9:39
Add a comment  | 

3 Answers 3

Reset to default 12

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>
        </>
    );
}

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>

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;
发布评论

评论列表(0)

  1. 暂无评论