在不重新加载页面的情况下测量组件的宽度

Measure width of component without re-loading the page

我正在使用这个钩子来测量 div 组件 (https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node) 的大小:

function useClientRect() {
  const [rect, setRect] = React.useState(null);
  const ref = React.useCallback(node => {
    if (node !== null) {
      setRect(node.getBoundingClientRect());
    }
  }, []);
  return [rect, ref];
}

这是我的组件,经过简化。我将它与 React Map GL 一起使用,因为无法根据 % 设置视口的宽度。我找不到其他方法...getBoundsForPoints 是一个函数,用于获取地图视口的限制,给定坐标数组和以像素为单位的地图宽度。

function Mapas(props){  
    const [rect,ref] = useClientRect() 
    useEffect(()=>{
      const coordinates = [some coordinates]
      let new_viewport
      if (rect!==null){ //Here is when sometimes I get rect===null 
        new_viewport = getBoundsForPoints(coordinates, rect.width) 
      } else {
        new_viewport = getBoundsForPoints(coordinates, 500)
      }
      setViewport(new_viewport) 
    },[rect]) 

    return (
        <MDBContainer>
            <MDBRow>
              <MDBCol md='12' xl='8'>
                 <div ref={ref}> {/*Elemento referenciado */}
                  <ReactMapGL
                    {...viewport}
                    mapboxApiAccessToken='xxx'}
                    mapStyle='xxx'
                    onViewportChange={nextViewport => setViewport({...nextViewport})}
                    >
                  </ReactMapGL>
                </div>
              </MDBCol>
              <MDBCol xl='4' md='12'>
                 Other stuff
              </MDBCol>
            </MDBRow>       
      </MDBContainer>        
    )
}

export default Mapas

页面加载后工作正常;然而,当我从其他组件到达页面时,没有重新渲染,rect 为空。我怎样才能避免这种情况,谢谢!

import ResizeObserver from 'resize-observer-polyfill';

const measureSize = el => {
    return {
      height: el && el.current && Math.round(el.current.getBoundingClientRect().height),
      width: el && el.current && Math.round(el.current.getBoundingClientRect().width),
    };
  };
  const [size, setSize] = useState(measureSize(ref));

  useEffect(() => {
    setSize(measureSize(ref));

    const observer = new ResizeObserver(() => {
      window.requestAnimationFrame(() => {
        setSize(measureSize(ref));
      });
    });
    observer.observe(ref.current);

    return () => {
      observer.disconnect();
    };
  }, [ref]);

我使用 polyfill resize-observer-polyfill 以获得更强大的解决方案

我终于不得不使用钩子 useLayoutEffect,而不是 useEffect。钩子 (https://es.reactjs.org/docs/hooks-reference.html#uselayouteffect) 与 useEffect 类似,但在构建所有组件后是 运行,因此引用现在已正确定义并且可以测量组件的尺寸。 最终的简化代码如下:

function useClientRect() {
  const [rect, setRect] = React.useState(null);
  const ref = React.useCallback(node => {
    console.log('Node')
    console.log(node)
    if (node !== null) {
      setRect(node.getBoundingClientRect());
    }
  }, []);
  return [rect, ref];
}

function Mapas(props){  
    const [rect,ref] = useClientRect() 

    useLayoutEffect(()=>{
      const coordinates = [some coordinates]
      let new_viewport
      if (rect!==null){ //Here is when sometimes I get rect===null 
        new_viewport = getBoundsForPoints(coordinates, rect.width) 
      } else {
        new_viewport = getBoundsForPoints(coordinates, 500)
      }
      setViewport(new_viewport) 
    },[rect]) 

    return (
        <MDBContainer>
            <MDBRow>
              <MDBCol md='12' xl='8'>
                 <div ref={ref}> {/*Elemento referenciado */}
                  <ReactMapGL
                    {...viewport}
                    mapboxApiAccessToken='xxx'}
                    mapStyle='xxx'
                    onViewportChange={nextViewport => setViewport({...nextViewport})}
                    >
                  </ReactMapGL>
                </div>
              </MDBCol>
              <MDBCol xl='4' md='12'>
                 Other stuff
              </MDBCol>
            </MDBRow>       
      </MDBContainer>        
    )
}

export default Mapas