如何在渲染之前获取反应组件的大小(高度/宽度)?

新手上路,请多包涵

我有一个反应组件,它需要 在渲染之前提前知道它的尺寸。

当我在 jquery 中制作小部件时,我可以 $('#container').width() 并在构建组件时提前获取容器的宽度。

 <div id='container'></div>

这些容器的尺寸是在 CSS 中定义的,以及页面上的许多其他容器。谁定义了 React 中组件的高度和宽度以及位置?我习惯 CSS 这样做并能够访问它。但在 React 中,我似乎只能在组件渲染后访问该信息。

原文由 Shai UI 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.6k
2 个回答

下面的示例 使用反应钩子 useEffect

这里的 工作示例

import React, { useRef, useLayoutEffect, useState } from "react";

const ComponentWithDimensions = props => {
  const targetRef = useRef();
  const [dimensions, setDimensions] = useState({ width:0, height: 0 });

  useLayoutEffect(() => {
    if (targetRef.current) {
      setDimensions({
        width: targetRef.current.offsetWidth,
        height: targetRef.current.offsetHeight
      });
    }
  }, []);

  return (
    <div ref={targetRef}>
      <p>{dimensions.width}</p>
      <p>{dimensions.height}</p>
    </div>
  );
};

export default ComponentWithDimensions;

一些注意事项

useEffect 将无法检测到它自己对宽度和高度的影响

例如,如果您在未指定初始值的情况下更改状态挂钩(例如 const [dimensions, setDimensions] = useState({}); ),则在渲染时高度将读取为零,因为

  • 没有通过 css 在组件上设置显式高度
  • 只有在 useEffect 之前绘制的内容才能用于测量宽度和高度
  • 唯一的组件内容是具有高度和宽度变量的 p 标签,当为空时,组件的高度为零
  • 设置新的状态变量后,useEffect 不会再次触发。

在大多数用例中,这可能不是问题,但我想我会包括它,因为它对调整窗口大小有影响。

窗口大小调整

我还认为原始问题中存在一些未探索的含义。我遇到了为动态绘制的组件(例如图表 )调整窗口大小的问题

即使未指定此答案,我也将其包括在内, 因为

  1. 可以公平地假设,如果应用程序需要尺寸,那么在调整窗口大小时可能需要它们。
  2. 只有 state 或 props 的变化会导致重绘,所以 还需要一个 window resize listener 来监控维度的变化
  3. 如果在每个窗口调整大小事件中使用更复杂的组件重新绘制组件,则会影响性能。我发现引入 setTimeout 和 clearInterval 很有帮助。我的组件包含一个图表,所以我的 CPU 飙升,浏览器开始爬行。下面的解决方案为我解决了这个问题。

下面的代码, 这里 的工作示例

import React, { useRef, useLayoutEffect, useState } from 'react';

const ComponentWithDimensions = (props) => {
    const targetRef = useRef();
    const [dimensions, setDimensions] = useState({});

    // holds the timer for setTimeout and clearInterval
    let movement_timer = null;

    // the number of ms the window size must stay the same size before the
    // dimension state variable is reset
    const RESET_TIMEOUT = 100;

    const test_dimensions = () => {
      // For some reason targetRef.current.getBoundingClientRect was not available
      // I found this worked for me, but unfortunately I can't find the
      // documentation to explain this experience
      if (targetRef.current) {
        setDimensions({
          width: targetRef.current.offsetWidth,
          height: targetRef.current.offsetHeight
        });
      }
    }

    // This sets the dimensions on the first render
    useLayoutEffect(() => {
      test_dimensions();
    }, []);

    // every time the window is resized, the timer is cleared and set again
    // the net effect is the component will only reset after the window size
    // is at rest for the duration set in RESET_TIMEOUT.  This prevents rapid
    // redrawing of the component for more complex components such as charts
    window.addEventListener('resize', ()=>{
      clearInterval(movement_timer);
      movement_timer = setTimeout(test_dimensions, RESET_TIMEOUT);
    });

    return (
      <div ref={ targetRef }>
        <p>{ dimensions.width }</p>
        <p>{ dimensions.height }</p>
      </div>
    );
}

export default ComponentWithDimensions;

回复:窗口大小调整超时- 在我的情况下,我正在绘制一个带有这些值下游图表的仪表板,我发现 RESET_TIMEOUT 上的 100 毫秒似乎在 CPU 使用率和响应能力之间取得了很好的平衡。我没有关于什么是理想的客观数据,所以我把它作为一个变量。

原文由 Shane 发布,翻译遵循 CC BY-SA 4.0 许可协议

正如已经提到的,在将元素呈现给 DOM 之前,您无法获得任何元素的尺寸。在 React 中你可以做的是只渲染一个容器元素,然后在 componentDidMount 中获取它的大小,然后渲染其余内容。

我做了一个 工作示例

请注意,在 setState componentDidMount 是一种反模式,但在这种情况下没问题,因为这正是我们想要实现的目标。

干杯!

代码:

 import React, { Component } from 'react';

export default class Example extends Component {
  state = {
    dimensions: null,
  };

  componentDidMount() {
    this.setState({
      dimensions: {
        width: this.container.offsetWidth,
        height: this.container.offsetHeight,
      },
    });
  }

  renderContent() {
    const { dimensions } = this.state;

    return (
      <div>
        width: {dimensions.width}
        <br />
        height: {dimensions.height}
      </div>
    );
  }

  render() {
    const { dimensions } = this.state;

    return (
      <div className="Hello" ref={el => (this.container = el)}>
        {dimensions && this.renderContent()}
      </div>
    );
  }
}

原文由 Stanko 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题