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:
FastComponent
10 msSlowComponent
50 msApp
52 msBut in reality what I get is both fast and slow component componentDidMount
callbacks are fired at the same time, i.e.
FastComponent
50 msSlowComponent
51 msApp
52 msCurrent 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"));
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:
UNSAFE_componentWillMount
methodUNSAFE_componentWillMount
methodrender
method(here your slow component expensive render method will kick-in)componentDidMount
methodcomponentDidMount
methodThis 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With