React: componentDidMount is called before the visual is rendered

Created on 4 Feb 2016  Â·  5Comments  Â·  Source: facebook/react

I am trying to get visual attributes (width to be precise) of a component once it's rendered and CSS is applied. I've been trying with componentDidMount but width is still 0, I am not sure what I'm doing wrong.

output:

rendering
component did mount. The ribbon's width is 0

And after that, if I click on my header's button to "re-route" back to the same page (i.e. trigger an update), then the componentDidUpdate is called with the proper width. Am I missing something or is this a bug?

Thanks you lots folks.

import React, {Component} from 'react';

export default class RibbonHeader extends Component {
  componentDidMount() {
    console.log('component did mount. The ribbon\'s width is', this.refs.ribbonSvg.width.animVal.value); // is 0
    this.paintInSVG();
  }

  shouldComponentUpdate() {
    console.log('should component update'); // never called
    return true;
  }

  componentDidUpdate() {
    // Not automatically called, but if I generate a routing event, it updates
    console.log('component did update. The ribbon\'s width is', this.refs.ribbonSvg.width.animVal.value);  // width is 1020px (good)
    this.paintInSVG();
  }

  paintInSVG() {
    const refRibbon = this.refs.ribbonSvg;
    doSomeSVGMagicThatDependsOnTheWidth(refRibbon);
  }

  render() {
    console.log('rendering');

    return (
      <svg ref="ribbonSvg" styles={{width: '100%'}}></svg>
    );
  }
}

EDIT:
Adding a timeout to componentDidMount does the job, although this is probably not normal

  componentDidMount() {
    setTimeout(() => {
      this.paintInSVG();
    }, 100);
  }
Question

All 5 comments

Hmm...

¯_(ツ)_/¯

Try:
console.log('component did mount. The ribbon\'s width is', this.refs.ribbonSvg.offsetWidth);

Slightly better, but still not what I'm looking for.
(before and after timeout)

component did mount. The ribbon's width is 300
component did mount. The ribbon's width is 1000

Hmm. I get 300 both times, even when I check a full second later (http://jsfiddle.net/r94vwoxr/).

Honestly, I'm not an SVG expert, but this looks like a usage question. React is responsible for creating the DOM/SVG nodes before calling componentDidMount, but everything I'm seeing here seems to indicate React has done that correctly. Beyond that, it's up to the browser.

To me, this looks like a usage question, rather than a bug in the React core. Usage questions are better answered on sites like StackOverflow, as we try to use github issues for tracking bugs in the React core, and I don't see anything here to indicate that React isn't doing it's job. I suspect some kind of race condition, where the SVG nodes are created but the browser is still doing SVG layout or something. Perhaps there is a command to force SVG to flush, or perhaps it's a bug in chrome, or something. If you are able to come up with a clear example/jsfiddle that demonstrates a bug in the React core, let me know and we can re-investigate. Otherwise, I'd recommend asking on StackOverflow.

cc @sebmarkbage for ideas.

I am having the same problem. I am trying to get the width of a drop-down menu in order to put it below a header. The width is base on email's length but inside componenDidMount I am getting a slightly different width.

I could use setTimeout but I don't feel comfortable doing that.

Here is the code: https://codesandbox.io/s/jnpzn15vxw

Was this page helpful?
0 / 5 - 0 ratings