44

This is a recurring problem I have with React. The componentDidMount method is guaranteed to be fired when the component is rendered for the first time so it seems like a natural place to take DOM measurements like heights and offsets. However, many times I receive wrong style readings at this point of the component's lifecycle. The component is in the DOM, when I break with the debugger, but it's not yet painted on the screen. I get this problem with elements that have width/height set to 100% mostly. When I take measurements in componentDidUpdate - everything works fine, but this method will not fire at the initial render of the component.

So my question is - when exactly is componentDidMount fired because it's obviously not fired after all the browser paints are done.

EDIT: This Stackoverflow issue deals with the same subject:

It also references this github conversation that explains what happens

Community
  • 1
  • 1
disc0dancer
  • 9,185
  • 11
  • 36
  • 46
  • This seems relevant: http://stackoverflow.com/questions/25371926/using-react-how-can-i-get-the-width-of-an-auto-sized-dom-element – lux Mar 17 '16 at 03:30

5 Answers5

46

Inside a react component tree, componentDidMount() is fired after all children components have also been mounted. This means, that any component's componentDidMount() is fired before its parent has been mounted.

So if you want to measure DOM position and sizes etc, using componentDidMount() of a child component is an unsafe place/ time to do this.

In your case: to get accurate reading from 100% width/height components, a safe place to take such measurements would be inside the componentDidMount() of the top react component.
100% is a width/height relative to the parent/ container. So measurements can only be taken after the parent has been mounted too.

wintvelt
  • 13,855
  • 3
  • 38
  • 43
  • 1
    This seems to answer my question! However, I'm a bit confused on how can `componentDidMount` be fired before the parent container is mounted. If there is no DOM element representing the parent, where is the component's element placed in the DOM? – disc0dancer Mar 17 '16 at 11:21
  • 1
    Some searching around does not provide any obvious answers to this mystery, I am afraid. Probably because of react's under the hood workings. My guess is that the parent element is in already in the DOM, but that formating is only guaranteed to be finished after `componenDidMount()` is fired inside the top component. – wintvelt Mar 17 '16 at 11:50
  • I'm also trying to figure this out. When I researched, I found that react follow a graph traversal method called "Depth First Search". Check this video. https://www.youtube.com/watch?v=pcKY4hjDrxk – dmcshehan Apr 06 '19 at 01:22
7

As you may know, componentDidMount is triggered only once immediately after the initial rendering.

Since you are taking measurements, it would seem you would want to also trigger your measuring when componentDidUpdate in case your measurements change when your component updates.

Please note that componentDidUpdate does not occur for the initial render so you likely need both lifecycle events to trigger your measurement handling. See if this second event triggers for you and if it has different measurements.

In my opinion you should avoid using setTimeout or requestAnimationFrame when possible.

React Lifecycle Reference.

Scott
  • 3,736
  • 2
  • 26
  • 44
5

It is called only once when the component mounted. That’s the perfect time to do an asynchronous request to fetch data from an API. The fetched data would get stored in the internal component state to display it in the render() lifecycle method.

Simple: It Run After Render Functions

Saad pasta
  • 51
  • 1
  • 5
2

If you want to respond to something being mounted in the DOM, the most reliable way to do that is with a ref callback. For example:

render() {
  return (
    <div
      ref={(el) => {
        if (el) {
          // el is the <div> in the DOM. Do your calculations here!
        }
      }}
    ></div>
  );  
}
Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196
0

you can try delaying logic in componentDidMount() with requestAnimationFrame(). the logic should occur after the next paint.

however, we'd need to know more about your code to see why the nodes haven't been painted. i've never hit that problem. componentDidMount() fires right after the dom nodes are added to the page, but not necessarily after they have been painted.

Jonathan Ong
  • 19,927
  • 17
  • 79
  • 118