4

Is componentDidMount lifecycle method independent from sibling components? From example below it seems like it is invoked only after all sibling components have been mounted.

Let's say we have a top level component which renders 2 child components, first having simple render() and another having relatively slower render().

Sample to reproduce: https://codesandbox.io/s/j43klml9py?expanddevtools=1

TL;DR:


class SlowComponent extends Component {
  componentDidMount() {
    // perf mark
  }

  render() {
    // Simulate slow render
    // Takes 50ms
    return <h3>Slow component</h3>;
  }
}

class FastComponent extends Component {
  componentDidMount() {
    // perf mark
  }

  render() {
    return <h3>Fast component</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    // perf mark start
  }

  componentDidMount() {
    // perf mark
    // measure all marks and print
  }

  render() {
    return (
      <div>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

I expect componentDidMount timings to be like this:

  1. FastComponent 10 ms
  2. SlowComponent 50 ms
  3. App 52 ms

But in reality what I get is both fast and slow component componentDidMount callbacks are fired at the same time, i.e.

  1. FastComponent 50 ms
  2. SlowComponent 51 ms
  3. App 52 ms

Current sample and repro code uses mount callback but same applies to componentDidUpdate.

Full source:

import ReactDOM from "react-dom";
import React, { Component } from "react";

class SlowComponent extends Component {
  componentDidMount() {
    performance.mark("slow-mounted");
  }

  render() {
    // Simulate slow render
    for (var i = 0; i < 10000; i++) {
      for (var j = 0; j < 100; j++) {
        const b = JSON.parse(
          JSON.stringify({
            test: "test" + i,
            test1: i * i * i
          })
        );
      }
    }
    return <h3>Slow component</h3>;
  }
}

class FastComponent extends Component {
  componentDidMount() {
    performance.mark("fast-mounted");
  }

  render() {
    return <h3>Fast component</h3>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);

    performance.mark("init");
  }

  componentDidMount() {
    performance.mark("app-mounted");

    performance.measure("slow", "init", "slow-mounted");
    performance.measure("fast", "init", "fast-mounted");
    performance.measure("app", "init", "app-mounted");

    console.clear();

    console.log(
      "slow",
      Math.round(performance.getEntriesByName("slow")[0].duration)
    );
    console.log(
      "fast",
      Math.round(performance.getEntriesByName("fast")[0].duration)
    );
    console.log(
      "app",
      Math.round(performance.getEntriesByName("app")[0].duration)
    );

    performance.clearMarks();
    performance.clearMeasures();
  }

  render() {
    return (
      <div>
        <h1>Demo</h1>
        <FastComponent />
        <SlowComponent />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
Lev
  • 3,719
  • 6
  • 41
  • 56
  • Quick answer: they are not independent. See https://stackoverflow.com/questions/32814970/is-componentdidmount-of-parent-called-after-all-componentdidmount-of-children – keul Sep 05 '18 at 12:43
  • That one is talking about parent/child component dependency, my current question is regarding children components. – Lev Sep 05 '18 at 14:06

4 Answers4

5

I assume you are using the latest React version (16.5.0).

Assuming two components, one slow and one fast wrapped in a parent component the execution flow will be the following:

  1. Parent component calls its UNSAFE_componentWillMount method
  2. Iterates all children with the order they are defined in its render method and calls their UNSAFE_componentWillMount method
  3. Iterate all children and call their render method(here your slow component expensive render method will kick-in)
  4. Iterate all children and call their componentDidMount method
  5. Parent component calls its componentDidMount method

This whole process is being done synchronously.

Going back to your example, this is why you see almost the same timing to both your components, because all component lifecycle methods are called as soon as the work is completed for all components.

In general, conceptually React consists of 2 phases, "render" phase and "commit" phase. The "commit" phase is when React applies any changes, and in your example the lifecycle method componentDidMount is called in this phase.

You can follow this flow by debugging React, and viewing the stack trace which is the following:

componentDidMount
commitLifeCycles
commitAllLifeCycles
callCallback
invokeGuardedCallbackDev
invokeGuardedCallback
commitRoot
Sotiris Kiritsis
  • 3,178
  • 3
  • 23
  • 31
1

The simple answer

All components will fire componentDidMount method after all renders are completed. Because before to mount elements to DOM we need to create them.

sultan
  • 4,543
  • 2
  • 24
  • 32
1

As per your life cycle order, your componentDidMount will be called (as the name itself suggests) after your component has been mounted. Which will be after the DOM has been mounted, which in doing so will call your render.

Now assuming if there is no I/O i.e, async event happening in your child component anywhere.

(and even if there are any async event happening, the functions will be executed but the call stack will maintain the execution in the event loop.. but this is a separate discussion)

-- Parent
  -- Child-1
  -- Child-2

Order of execution

Parent constructor()
Child 1 constructor()
Child 1 render()
Child 1 componentDidMount()
Child 2 constructor()
Child 2 render()
Child 2 componentDidMount()
Parent componentDidMount()

As per the ReactJS docs the mounting triggers in order are

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

Reference of mounting order from react documentation Mounting React Docs

On a side note, another detailed read on the order of execution. Understanding React — Component life-cycle

Adeel Imran
  • 13,166
  • 8
  • 62
  • 77
0

Yes, ComponentDidMount lifecycle method is independent from sibling components. It depends on how your render takes time because it invokes after your render complete. But the component mounts in same order as they are in parent component. E.g - In above example first fast component mounts then second component.

And you can't calculate how much time a function takes to iterate a loops. So here your assumption of calculating time of slow component's ComponentDidMount lifecycle method was wrong. You can't expect specific time estimate based on number of iteration without profiling.

shah chaitanya
  • 521
  • 4
  • 9