R. Gulbrandsen
R. Gulbrandsen

Reputation: 3778

How to Get: Component Width After Render in React

I'm trying to create a general purpose component, that I can reuse in other applications. I need to know the width of the component after render so that I can modify the content of the component.

I've been trying to use the different life cycles in react without success.

componentDidUpdate() {
  console.log('width', this.element.offsetWidth);
}

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

When I try this I get the width of the screen, but if I change the size of the window, I get the width of the component. See the Chrome Log:

Chrome log

ComponentDidMount executes before render so this.element is undefined.

I've also attempted to use different libraries from npm to solve this without luck.

Futher information: The component has to work inside a Bootstrap column, at different widths.

render() {
  <Row>
    <Col sm={3} />
      <MyComponent />
    </Col>
    <Col sm={9} />
      <MyComponent />
    </Col>
  <Row>
}

Clarification I do not want to resize the window, and I apologize for not being clear. The only reason for me to mention the resizing is that when the DOM has been created and I resize, I get the correct value in offsetWidth. I'm looking for a solution where I get the correct value without resizing. Either a post render function call, listeners, some react magic, or other solutions. My problem is my lack of knowledge with the virtual vs. real DOM.

Upvotes: 7

Views: 28280

Answers (4)

Jason
Jason

Reputation: 719

This "SizeMe" component helped me dynamically resize my d3 chart, thanks.

In case it may help someone, this my code from the calling Parent component:

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

With the CSS:

#my_chart_div {
  width: 100%;
}

Upvotes: 0

R. Gulbrandsen
R. Gulbrandsen

Reputation: 3778

I was unable to solve this problem with the answers given here. I only got the width of the browser window and not the component within. After some research, it looks like I'm having a chicken or the egg problem with the render. After some more research, I found react-sizeme that solves the issue.

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);

Which will produce the following when it first renders

enter image description here

Upvotes: 14

Dimitar Christoff
Dimitar Christoff

Reputation: 26165

Whereas this is not an answer to your question directly, it's a solution to your problem that does not add resizing and dirty logic inside of your components.

I'd recommend something like https://github.com/digidem/react-dimensions - which is a HOC wrapper and will listen to global resize events and send you props - containerWidth and containerHeight - I tend to use it a lot when working with SVG, canvas and data grids in react that need to remain responsive and need to know the element's size.

As for lifecycles - things like componentDidUpdate may not behave the way you think it should. Mount is a one-off. Read this comment - https://github.com/facebook/react/issues/2659#issuecomment-66165159

Upvotes: 1

Anatoly Strashkevich
Anatoly Strashkevich

Reputation: 1914

If you need to hold component width state you can do something like this:

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
                }); 
              }
          );

};    

You can replace Observable with event listener. Alternatively you can update bounding box attached to class and derive state from it somewhere else.

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

Upvotes: 4

Related Questions