Is ponentDidMount
lifecycle method independent from sibling ponents? From example below it seems like it is invoked only after all sibling ponents have been mounted.
Let's say we have a top level ponent which renders 2 child ponents, first having simple render()
and another having relatively slower render()
.
Sample to reproduce:
TL;DR:
class SlowComponent extends Component {
ponentDidMount() {
// perf mark
}
render() {
// Simulate slow render
// Takes 50ms
return <h3>Slow ponent</h3>;
}
}
class FastComponent extends Component {
ponentDidMount() {
// perf mark
}
render() {
return <h3>Fast ponent</h3>;
}
}
class App extends Component {
constructor(props) {
super(props);
// perf mark start
}
ponentDidMount() {
// perf mark
// measure all marks and print
}
render() {
return (
<div>
<FastComponent />
<SlowComponent />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
I expect ponentDidMount
timings to be like this:
FastComponent
10 msSlowComponent
50 msApp
52 ms
But in reality what I get is both fast and slow ponent ponentDidMount
callbacks are fired at the same time, i.e.
FastComponent
50 msSlowComponent
51 msApp
52 ms
Current sample and repro code uses mount callback but same applies to ponentDidUpdate
.
Full source:
import ReactDOM from "react-dom";
import React, { Component } from "react";
class SlowComponent extends Component {
ponentDidMount() {
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 ponent</h3>;
}
}
class FastComponent extends Component {
ponentDidMount() {
performance.mark("fast-mounted");
}
render() {
return <h3>Fast ponent</h3>;
}
}
class App extends Component {
constructor(props) {
super(props);
performance.mark("init");
}
ponentDidMount() {
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"));
Is ponentDidMount
lifecycle method independent from sibling ponents? From example below it seems like it is invoked only after all sibling ponents have been mounted.
Let's say we have a top level ponent which renders 2 child ponents, 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 {
ponentDidMount() {
// perf mark
}
render() {
// Simulate slow render
// Takes 50ms
return <h3>Slow ponent</h3>;
}
}
class FastComponent extends Component {
ponentDidMount() {
// perf mark
}
render() {
return <h3>Fast ponent</h3>;
}
}
class App extends Component {
constructor(props) {
super(props);
// perf mark start
}
ponentDidMount() {
// perf mark
// measure all marks and print
}
render() {
return (
<div>
<FastComponent />
<SlowComponent />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
I expect ponentDidMount
timings to be like this:
FastComponent
10 msSlowComponent
50 msApp
52 ms
But in reality what I get is both fast and slow ponent ponentDidMount
callbacks are fired at the same time, i.e.
FastComponent
50 msSlowComponent
51 msApp
52 ms
Current sample and repro code uses mount callback but same applies to ponentDidUpdate
.
Full source:
import ReactDOM from "react-dom";
import React, { Component } from "react";
class SlowComponent extends Component {
ponentDidMount() {
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 ponent</h3>;
}
}
class FastComponent extends Component {
ponentDidMount() {
performance.mark("fast-mounted");
}
render() {
return <h3>Fast ponent</h3>;
}
}
class App extends Component {
constructor(props) {
super(props);
performance.mark("init");
}
ponentDidMount() {
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"));
Share
Improve this question
asked Sep 5, 2018 at 12:40
LevLev
3,9246 gold badges44 silver badges59 bronze badges
2
- Quick answer: they are not independent. See stackoverflow./questions/32814970/… – keul Commented Sep 5, 2018 at 12:43
- That one is talking about parent/child ponent dependency, my current question is regarding children ponents. – Lev Commented Sep 5, 2018 at 14:06
4 Answers
Reset to default 5 +500I assume you are using the latest React version (16.5.0).
Assuming two ponents, one slow and one fast wrapped in a parent ponent the execution flow will be the following:
- Parent ponent calls its
UNSAFE_ponentWillMount
method - Iterates all children with the order they are defined in its render method and calls their
UNSAFE_ponentWillMount
method - Iterate all children and call their
render
method(here your slow ponent expensive render method will kick-in) - Iterate all children and call their
ponentDidMount
method - Parent ponent calls its
ponentDidMount
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 ponents, because all ponent lifecycle methods are called as soon as the work is pleted for all ponents.
In general, conceptually React consists of 2 phases, "render" phase and "mit" phase.
The "mit" phase is when React applies any changes, and in your example the lifecycle method ponentDidMount
is called in this phase.
You can follow this flow by debugging React, and viewing the stack trace which is the following:
ponentDidMount
mitLifeCycles
mitAllLifeCycles
callCallback
invokeGuardedCallbackDev
invokeGuardedCallback
mitRoot
As per your life cycle order, your ponentDidMount will be called (as the name itself suggests) after your ponent 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 ponent 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 ponentDidMount()
Child 2 constructor()
Child 2 render()
Child 2 ponentDidMount()
Parent ponentDidMount()
As per the ReactJS docs the mounting triggers in order are
- constructor()
- static getDerivedStateFromProps()
- render()
- ponentDidMount()
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
The simple answer
All ponents will fire ponentDidMount
method after all renders are pleted. Because before to mount elements to DOM we need to create them.
Yes, ComponentDidMount lifecycle method is independent from sibling ponents. It depends on how your render takes time because it invokes after your render plete. But the ponent mounts in same order as they are in parent ponent. E.g - In above example first fast ponent mounts then second ponent.
And you can't calculate how much time a function takes to iterate a loops. So here your assumption of calculating time of slow ponent's ComponentDidMount lifecycle method was wrong. You can't expect specific time estimate based on number of iteration without profiling.