如何使用 Hooks 在 React 中获取父级宽度/高度?

Ota*_*der 15 javascript reactjs

我正在创建一个组件,我需要获取它的父<div>宽度和高度。我正在使用 Hooks,所以我所有的组件都是函数。我已经阅读了一些使用类的示例,但这不适用于我的组件。

所以我有这个组件:

export default function PlantationMap(props) {
    <div className="stage-canvas">
        <Stage
          width={window.innerWidth * 0.5}
          height={window.innerHeight * 0.5}
          onWheel={handleWheel}
          scaleX={stage.stageScale}
          scaleY={stage.stageScale}
          x={stage.stageX}
          y={stage.stageY}
          draggable
        / >
    </div>
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能获得<div>要使用的高度和宽度<Stage width={} height={} />

非常感谢您提前

编辑:我尝试使用useRef()钩子,如下所示:

const div = useRef();

return (
  <div ref={div}>
  ...
  </div>
)
Run Code Online (Sandbox Code Playgroud)

但我无法访问该div.current对象

Bru*_*ith 15

我认为 useCallback 是您想要使用的,因此您可以在更改时获取宽度和高度。

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

  return (
    <div ref={div}>
    ...
    </div>
  )
Run Code Online (Sandbox Code Playgroud)


Dan*_*ins 14

您可以使用内置的ResizeObserver

export default function PlantationMap(props) {
    const [width, setWidth] = useState(100);
    const [height, setHeight] = useState(100);

    useEffect(() => {
        const resizeObserver = new ResizeObserver((event) => {
            // Depending on the layout, you may need to swap inlineSize with blockSize
            // https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentBoxSize
            setWidth(event[0].contentBoxSize[0].inlineSize);
            setHeight(event[0].contentBoxSize[0].blockSize);
        });

        resizeObserver.observe(document.getElementById("div1"));
    });

    return (
        <div id="div1" className="stage-canvas">
            <Stage
                width={width * 0.5}
                height={height * 0.5}
                onWheel={handleWheel}
                scaleX={stage.stageScale}
                scaleY={stage.stageScale}
                x={stage.stageX}
                y={stage.stageY}
                draggable
            / >
        </div>
    );
}
Run Code Online (Sandbox Code Playgroud)

  • 每次渲染此组件时,此解决方案都会创建一个新的 ResizeObserver。您需要向 useEffect() 添加一个依赖数组。另外,您需要在效果的清理回调中断开ResizeObserver的连接。 (4认同)

Eri*_*dán 8

使用useRef钩子声明一个引用,然后读取current.offsetHeightcurrent.offsetWidth属性。

这是代码:

import React, { useEffect, useRef } from 'react';

const PlantationMap = (props) => {

    const stageCanvasRef = useRef(null);

    // useEffect will run on stageCanvasRef value assignment
    useEffect( () => {

        // The 'current' property contains info of the reference:
        // align, title, ... , width, height, etc.
        if(stageCanvasRef.current){

            let height = stageCanvasRef.current.offsetHeight;
            let width  = stageCanvasRef.current.offsetWidth;
        }

    }, [stageCanvasRef]);

    return(
        <div className = "stage-canvas" ref = {stageCanvasRef}>
            <Stage
              width={window.innerWidth * 0.5}
              height={window.innerHeight * 0.5}
              onWheel={handleWheel}
              scaleX={stage.stageScale}
              scaleY={stage.stageScale}
              x={stage.stageX}
              y={stage.stageY}
              draggable
            / >
        </div>);

}

export default PlantationMap;
Run Code Online (Sandbox Code Playgroud)


AWo*_*olf 5

我认为 ResizeObserver 是 Dan 的答案中提到的方法。我只是不会使用document.getElementById. 要么使用useMeasurereact -use,要么自己创建一切。

有两种情况:

  1. 组件包含您想要观察的容器
  2. 组件是子组件,没有容器引用

至 1 - 可直接访问的参考

useRef在这种情况下,您可以在组件中创建引用并在 处使用它resizeObserver.observe(demoRef.current)

import "./styles.css";
import React, { useEffect, useRef, useState } from "react";

const DisplaySize = ({ width, height }) => (
  <div className="centered">
    <h1>
      {width.toFixed(0)}x{height.toFixed(0)}
    </h1>
  </div>
);

const Demo = () => {
  const [width, setWidth] = useState(100);
  const [height, setHeight] = useState(100);
  const demoRef = useRef();

  useEffect(() => {
    const resizeObserver = new ResizeObserver((event) => {
      // Depending on the layout, you may need to swap inlineSize with blockSize
      // https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentBoxSize
      setWidth(event[0].contentBoxSize[0].inlineSize);
      setHeight(event[0].contentBoxSize[0].blockSize);
    });

    if (demoRef) {
      resizeObserver.observe(demoRef.current);
    }
  }, [demoRef]);

  return (
    <div ref={demoRef} className="App">
      <DisplaySize width={width} height={height} />
    </div>
  );
}; //);

export default function App() {
  return <Demo />;
}
Run Code Online (Sandbox Code Playgroud)

至 2 - 无法直接访问的容器的引用:

这种情况可能发生得更频繁,并且需要的代码稍微多一些。您需要使用 来将引用从父组件传递到子组件React.forwardRef

演示代码可以在下面或下面的Codesandbox中找到

代码中的一些话:

  • 在父组件中,您创建一个引用const containerRef = useRef()并在主容器中使用它<div ref={containerRef}/>。在引擎盖下它会做类似的事情ref => containerRef.current=ref
  • 接下来,将引用传递给Demo组件。

为什么不使用React.createRef

这也可以,但它会在应用程序的每个渲染上重新创建引用。请查看此处,useRef了解和之间的区别的解释createRef

简而言之,与功能组件一起使用并与基于类的组件一起useRef使用。createRef

const {useEffect, useRef, useState} = React;

const DisplaySize = ({ width, height }) => (
  <div className="centered">
    <h1>
      {width.toFixed(0)}x{height.toFixed(0)}
    </h1>
  </div>
);

const Demo = React.forwardRef((props, ref) => {
  const [width, setWidth] = useState(100);
  const [height, setHeight] = useState(100);
  useEffect(() => {
    
    const resizeObserver = new ResizeObserver((event) => {
      // Depending on the layout, you may need to swap inlineSize with blockSize
      // https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentBoxSize
      setWidth(event[0].contentBoxSize[0].inlineSize);
      setHeight(event[0].contentBoxSize[0].blockSize);
    });

    if (ref && ref.current) {
      resizeObserver.observe(ref.current);
    }
  }, [ref]);

  return <DisplaySize width={width} height={height} />;
});

function App() {
  const containerRef = useRef();
  return (
    <div ref={containerRef} className="App">
      <Demo ref={containerRef} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App />,
  rootElement
);
Run Code Online (Sandbox Code Playgroud)
/* apply a natural box layout model to all elements, but allowing components to change */
html {
  box-sizing: border-box;
}
*,
*:before,
*:after {
  box-sizing: inherit;
}

html,
body {
  margin: 0;
  padding: 0;
}

.App {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  font-family: sans-serif;
  text-align: center;
  border: 4px solid red;
}

.centered {
  display: flex; /* establish flex container */
  flex-direction: column; /* make main axis vertical */
  justify-content: center; /* center items vertically, in this case */
  align-items: center; /* center items horizontally, in this case */
  height: 100%;
}
Run Code Online (Sandbox Code Playgroud)
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>


<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)

库 React 使用

还有一些有用的钩子React-use可以在这里提供帮助。

useWindowSize看起来useSize非常相似,但在查看源代码之后,第一个依赖于window.onresize事件并且需要更少的代码来实现。

useSize将在当前组件 ( z-index: -1) 下方添加一个 iframe 以通过事件跟踪大小resize,并且需要更多代码。它还增加了一点去抖功能setTimeout

因此,useWindowSize如果您只需要宽度/高度在第一次渲染时进行一些计算,并且useSize您想显示大小已更改,请使用。

使用窗口大小

如果您只需要获取窗口大小,则可以使用WindowSize 。他们通过 onresize 事件来完成此操作并检查document.addEventlistener('resize', resizeHandler)CodesandboxinnerWidth / innerHeight 演示

使用测量

要跟踪元素大小,useMeasure可以使用。它ResizeObserver在幕后使用,所以就像上面的代码一样,其中是ref您要跟踪的引用:

返回的第一个元素useMeasuresetRef方法。因此,您可以在组件中执行以下操作:

const [setRef, { width, height }] = useMeasure();
useEffect(() => {
    setRef(ref.current)
}, [])
Run Code Online (Sandbox Code Playgroud)

请看看下面的Codesandbox

使用大小

如果您想跟踪组件的大小,useSize可以使用文档中提到的方法。 Codesandbox 演示useSize


Riz*_*wan 1

据我所知,如果涉及风格只能通过以下方式注册:

<Stage style={{width:window.innerWidth * 0.5,height:width:window.innerWidth * 0.5}} />
Run Code Online (Sandbox Code Playgroud)