如何在React中获取组件渲染后的宽度

7
我正在尝试创建一个通用组件,可以在其他应用程序中重复使用。我需要知道组件渲染后的宽度,以便可以修改组件的内容。
我一直在尝试使用React中的不同生命周期,但没有成功。
componentDidUpdate() {
  console.log('width', this.element.offsetWidth);
}

render() {
  return (
    <div ref={(element) => {this.element = element }} />
  );
}

当我尝试这样做时,我得到了屏幕的宽度,但是如果我改变窗口的大小,我会得到组件的宽度。请参见Chrome日志:

Chrome log

ComponentDidMount在渲染之前执行,因此this.elementundefined

我也尝试使用不同的npm库来解决这个问题,但没有成功。

更多信息:该组件必须在不同宽度的Bootstrap列中工作。

render() {
  <Row>
    <Col sm={3} />
      <MyComponent />
    </Col>
    <Col sm={9} />
      <MyComponent />
    </Col>
  <Row>
}
澄清 我不想调整窗口大小,对于我的表述不够清晰我深感抱歉。我提到调整大小的唯一原因是当DOM已经创建并且我调整大小时,我可以在offsetWidth中获得正确的值。我正在寻找一种解决方案,在不调整大小的情况下获取正确的值。可以是后处理函数调用、监听器、一些react魔法或其他解决方案。我的问题是我对虚拟DOM和真实DOM的了解不足。

也许可以将 offsetWidth 存储在 state 中,每当组件改变大小时更新状态。 - Hemerson Carlin
如果我在匿名函数中设置状态setState({ element: element}),它会生成一个无限循环。我还看到在第一次执行时element是null - R. Gulbrandsen
我并没有说要将元素存储在状态中,而是要存储“width”值。因此,每次它发生变化时,您都可以设置状态值。 - Hemerson Carlin
4个回答

14

我无法通过这里给出的答案解决这个问题。我只获取到了浏览器窗口的宽度而不是其内部组件的宽度。经过一些研究,看起来我在渲染时遇到了鸡生蛋的问题。经过进一步的研究,我发现react-sizeme可以解决这个问题。

import React, { Component } from 'react';
import sizeMe from 'react-sizeme';

class MyComponent extends Component {
  render() {
    const { width } = this.props.size;

    return (
      <div style={{
        width: '100%',
        backgroundColor: '#eee',
        textAlign: 'center'
      }}>
        <span>My width is: {Math.floor(width)}px</span>
      </div>
    );
  }
}

export default sizeMe()(MyComponent);

当它首次呈现时,将会产生以下结果

输入图像描述


4
如果您需要保持组件宽度的状态,可以像这样操作:
componentDidMount(){ 

          this.boundingBox = this.element.getBoundingClientRect();

          this.setState({ 
              width:this.boundingBox.width
          }); 

          Observable.fromEvent(this.element,"resize")
          .subscribe(
              () => { 
                this.boundingBox = this.element.getBoundingClientRect();  
                this.setState({ 
                    width:this.boundingBox.width
                }); 
              }
          );

};    

您可以将Observable替换为事件监听器。 或者,您可以更新附加到类的边界框,并从其他地方派生状态。

componentDidUpdate(){ 
          this.boundingBox = this.element.getBoundingClientRect();
};     

componentDidMountrender 之前执行,因此 this.element 将始终为未定义。有什么建议吗? - R. Gulbrandsen
2
它不会渲染,只有在渲染完成后才执行。 - Anatoly Strashkevich
1
请查看文档:componentDidMount()会在组件挂载后立即调用。需要使用DOM节点进行初始化的内容应该放在这里,DOM节点在此处可用,您可以在发布之前测试答案,它是有效的。 - Anatoly Strashkevich

1

虽然这不是直接回答你的问题,但它是解决你的问题的一种方案,而且不会在组件内添加调整大小和脏逻辑。

我建议使用https://github.com/digidem/react-dimensions 这样的HOC包装器,它将监听全局调整大小事件并发送给您props-containerWidthcontainerHeight - 当我在react中使用SVG、画布和数据网格时,需要保持响应性并需要知道元素的大小。

至于生命周期 - 像componentDidUpdate之类的东西可能不会按照您认为它应该的方式运行。安装是一次性的。阅读这个注释 - https://github.com/facebook/react/issues/2659#issuecomment-66165159


react-dimension并不能解决我的问题,因为我不想通过调整屏幕大小来获取组件的宽度。感谢您提供的链接,现在我对componentDidMount有了更好的理解。 - R. Gulbrandsen
当组件挂载时,它会立即将prop发送给你,无需调整大小。 - Dimitar Christoff

0

这个“SizeMe”组件帮助我动态调整我的d3图表大小,谢谢。

如果有人需要的话,这是我从父组件中调用的代码:

import { SizeMe } from 'react-sizeme'
:    
<SizeMe monitorWidth>
  {({ size }) => (
    <MyChartComponent
      divChart = "my_chart_div"
      chartWidth ={parseInt(size.width)}
      someProps  ={my_obj.some_data}
    />
  )}
</SizeMe>

使用CSS:

#my_chart_div {
  width: 100%;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接