繁体   English   中英

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

[英]How to get parent width/height in React using Hooks?

我正在创建一个组件,我需要获取它的父<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>
}

如何获得<div>高度和宽度以在<Stage width={} height={} />中使用?

非常感谢您提前

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

const div = useRef();

return (
  <div ref={div}>
  ...
  </div>
)

但我无法访问div.current object

我认为 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>
  )

使用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;

据我所知,如果它与风格有关,只能通过以下方式注册:

<Stage style={{width:window.innerWidth * 0.5,height:width:window.innerWidth * 0.5}} />

您可以使用内置的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>
    );
}

我认为 ResizeObserver 是 go 的方式,如 Dan 的回答中所述。 我只是不会使用document.getElementById 要么使用react- use 中的useMeasure ,要么自己创建所有内容。

有两种情况:

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

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

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

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

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

代码中的一些话:

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

为什么不使用React.createRef

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

简而言之,将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 );
 /* 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%; }
 <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>

库反应使用

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

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

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

因此,如果您只需要宽度/高度来对第一次渲染进行一些计算,请使用useSize useWindowSize

使用窗口大小

如果您只需要获取 window 大小,则使用WindowSize 是go的方式。 他们正在使用document.addEventlistener('resize', resizeHandler)使用onresize 事件并检查innerWidth / innerHeight Codesandbox Demo

使用测量

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

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

const [setRef, { width, height }] = useMeasure();
useEffect(() => {
    setRef(ref.current)
}, [])

请看下面的Codesandbox

使用尺寸

如果您想跟踪组件的大小useSize可以按照文档中的说明使用。 useSize Demo 使用大小

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM