使用钩子调整大小时测量 React DOM 节点

san*_*oon 5 javascript reactjs react-hooks

我正在尝试在窗口调整大小事件上测量 React DOM 节点。我在React hooks-faq上使用过这个例子,但它只发生在第一次渲染中。如果我添加一个useEffect来监听调整大小,回调不会被调用?

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>
        </>
    );
}
Run Code Online (Sandbox Code Playgroud)

wat*_*ter 12

这是另一个解决方案。

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>
        </>
    );
}
Run Code Online (Sandbox Code Playgroud)


Kei*_*ith 3

就我个人而言,我会提取监听调整大小事件的部分。优点是可以再次用于其他用途。

因为调整大小是async,React 的一个技巧是让子级成为函数式返回,而不仅仅是返回 JSX。然后,您可以让 CaptureResize 组件调用该函数来获取它的 JSX,同时将大小传递给该函数。

下面是一个例子..

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'));
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)