7

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.

3
  • Maybe save offsetWidth in state and whenever you component changes its size you update the state Commented Sep 1, 2017 at 9:10
  • If I set state in the annon function setState({ element: element}) it'll generate an infinite loop. I also see that element is nullon first execute Commented Sep 1, 2017 at 9:19
  • I didn't say to store the element in the state but rather store the width value. So everytime it changes, you can set your state value. Commented Sep 1, 2017 at 12:57

4 Answers 4

14

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

Sign up to request clarification or add additional context in comments.

Comments

4

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

3 Comments

componentDidMount executes before render so this.element will always be undefined. suggestion?
it will not, it executes after render
check docs : componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here, DOM nodes are available here you can use them, i test answer before posting it works
1

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

2 Comments

see the clarification update on my question. react-dimension does not solve the problem as I dont want to resize the screen to get the width of the component. Thank you for your link, I have a better understanding of componentDidMount now.
you don't have to resize it, it sends you the prop as soon as it mounts.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.