React componentDidMount timing - javascript

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 ms
SlowComponent 50 ms
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.
FastComponent 50 ms
SlowComponent 51 ms
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"));

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:
Parent component calls its UNSAFE_componentWillMount method
Iterates all children with the order they are defined in its render method and calls their UNSAFE_componentWillMount method
Iterate all children and call their render method(here your slow component expensive render method will kick-in)
Iterate all children and call their componentDidMount method
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

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.

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

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.

Related

React lifecycle render called before componentDidUpdate

I am slightly confused with the way lifecycle order/execution happens and how things render in the DOM.
So if I take the below example, what I find is after 5 seconds, the control goes to render before it goes to componentDidUpdate. However, in render, it is able to print the updated state value i.e. XYZ even before componentDidUpdate is invoked. So how does this work and while componentDidUpdate says we can access DOM nodes inside it, I somehow was thinking that componentDidUpdate is when the state actually gets updated. Not sure where the difference is in terms of my understanding?
import React, { Component } from "react";
export default class getSnapshotBeforeUpdateMethod extends Component {
constructor(props) {
console.log("1 constructor");
super(props);
this.state = {
name: "ABC"
};
}
componentDidMount() {
console.log("2a componentDidMount");
setTimeout(() => {
console.log("2b Inside componentDidMount");
this.setState({ name: "XYZ" });
}, 3000);
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("3 getSnapshotBeforeUpdate");
document.getElementById("previous-state").innerHTML =
"The previous state was " + prevState.name;
}
componentDidUpdate() {
console.log("4 componentDidUpdate");
document.getElementById("current-state").innerHTML =
"The current state is " + this.state.name;
}
render() {
console.log("5 render");
return (
<>
<h5>This is a {this.state.name}</h5>
<p id="current-state"></p>
<p id="previous-state"></p>
</>
);
}
}
Codesandbox link
https://codesandbox.io/s/class-lifecycle-method-order-0zjk4?file=/src/App.js:0-1055
Perhaps a review of the react lifecycle diagram may help.
Take note that the render lifecycle method is invoked during the "render phase" when React is rendering out the virtualDOM in order to compute the diff it needs to commit, or flush, to the actual DOM. Note also that the functions/methods during this phase are pure and have no side effects, and may be paused, aborted, or restarted by React (emphasis mine).
Now note that componentDidUpdate occurs during the "commit phase" when the DOM has been updated and rendered.
Your console.log in the render method is an unintentional side-effect, but it's showing you when it was called relative to componentDidUpdate.
render is called once, or possibly multiple times, before componentDidUpdate is called once, per render cycle.

React Component Lifecycle

To my knowledge, when some events happen, react creates a virtual-DOM from scratch and compares it to the old virtual-DOM. If that is the case, when the render method is called, react should create components and return them to compare.
import React, { Component, Fragment } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class Demo extends Component {
constructor(props) {
console.log("constructor called");
super(props);
this.state = { dummy: 1, };
}
render = () => <button onClick={this.props.onChange}> button </button>;
}
class App extends Component {
state = { num: 0, };
onChange = () => {
this.setState({ num: this.state.num+1 }); // mutate state to trigger a render
}
render() {
console.log("rendered");
return (
<Fragment>
<Demo onChange={this.onChange} />
</Fragment>
);
}
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
A quick console log reveals that the constructor is called the first time to mount the Demo component onto the page. However, subsequent renders do not create new Demo objects that are compared with the old virtual-DOM (since the constructor is not called).
At first, my theory was that when the constructor of Demo is called, it then calls the super constructor to check if a similar object with the same props already exists. But that was not the case as proven by moving console.log("constructor called"); before calling the parent constructor.
So my question is how does react know not to create another object?
The key here is that Demo is not unmounted. When you first render the app, it renders and mounts the Demo component and passes the onChange prop. But, when the callback is invoked from Demo it sets the state on App. Calling setState in App doesn't unmount the Demo component, so there's no need to mount it again. When the component is mounted initially is when the constructor runs. If you had a toggle on the App component that would only show the component within render if a certain condition is true, that would trigger the component to unmount.
Check out this codesandbox and play around a little: https://codesandbox.io/s/lifecycle-methods-vivn6?file=/src/Lifecycle.js.
Also, this is a cool diagram to get a sense of what's happening: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
The main key is that the constructor runs when the component mounts. It will only run again if the component is unMounted from the DOM and then remounted later.

React SSR - Rendering a component following an async action

React's component lifecycle goes hand in hand with the DOM, so when I'm trying to render on the server side following an async action I'm encountering issues.
I have a component (let's call it the containing component) which is responsible for dynamically importing another component (let's call it the inner component) according to data passed in its props.
Once the import promise is resolved, I'd like to render the inner component inside of the containing component.
The problem is, I have no way of accessing the containing component's lifecycle.
render() is only triggered once upon the construction of the containing component.
Updating the component via forceUpdate() and setState() are not possible for the same reason componentDidMount() will never be called.
How do I render a component following an async action on the server side when using server rendering?
My code so far:
import React from 'react';
export default class ComponentLoader extends React.Component<{ component: string }, { component: any }> {
constructor(props) {
super(props);
this.state = {
component: null
}; //no-op
}
componentDidMount(): void {
//never called
}
componentWillMount(): void {
import('./' + this.props.component).then(module => {
this.forceUpdate(); //no-op
this.setState({ component: React.createElement(module.default) }); //no-op
});
}
render() {
//called only once during construction
return this.state.component || null; //this.state won't be available, nor will it matter if I access component any other way since render() won't be called again once that component is resolved anyway.
}
}
I ended up using loadable components, a different implementation than what you see in the comments though.
https://github.com/jamiebuilds/react-loadable
https://github.com/konradmi/react-loadable-ssr-code-splitting
On the server side it makes sure to only render the react components once all the dependencies are loaded async. That way the render is sync since that's the only thing that's supported at the moment (though there's a feature request for supporting async react ssr), but the loading is done async, so it doesn't block any other operations.

componentDidMount or componentWillMount which one I need to use

I created a a box similar to twitter using react. I was looking at the react documentation found several component life cycles but not sure which one I should use to improve my code performance: componentDidMount or componentWillMount?
When I type something in my text box I see an update in the console printing the text box value. Can anyone help me understand which method to use and when in this case?
https://jsfiddle.net/c9zv7yf5/2/
class TwitterBox extends React.Component {
constructor(props) {
super(props);
this.state = { enteredTextBoxvalue : '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({enteredTextBoxvalue: event.target.value});
if((event.target.value).length > 3) {
this.setState({className : 'wholeContainer'});
//console.log("long characters");
}
}
render() {
return (<div>Hello {this.props.name}
<textarea className={this.state.className}
value={this.state.enteredTextBoxvalue}
onChange = {this.handleChange}>
there should be only 140 characters
</textarea>
</div>);
}
}
ReactDOM.render(
<TwitterBox name="World" />,
document.getElementById('container')
);
componentWillMount is called right before the component gets rendered.
componentDidMount is called right after the component gets rendered.
If you need to prepare data you use componentWillMount.
componentDidMount is popularly used among sending api calls or grabbing data just right after the component renders and is highly recommended to use that.
componentWillMount:
This function is called right before the component’s first render, so at first glance it appears to be a perfect place to put data fetching logic
componentDidMount:
Using componentDidMount makes it clear that data won’t be loaded until after the initial render. This reminds you to set up initial state properly, so you don’t end up with undefined state that causes errors.
As part of your question is about performance you could consider also having a look at shouldComponentUpdate to avoid reconciliation.
componentWillMount is invoked immediately before mounting occurs. It is called before render().
componentDidMount is invoked immediately after a component is mounted.
componentWillMount
component is about to render, plays the same role as constructor
there is no component in DOM yet you cannot do anything involving DOM
manipulation
calling setState() synchronously will not trigger
re-render as the component is not rendered yet
I would not recommend calling async /api requests here (technically there is no guaranty they will finish before component will be mounted, in this case the your component will not be re-rendered to apply those data)
componentDidMount
component has been rendered, it already seats in the DOM
you can perform manipulations involving DOM elements here (e.g. initialize third-party plugin)
call async /api requests, etc.

React create class confusion with extends Components

I used to do React.createClass but since I heard it's faster with extends component and since I start to play with babel now, I would like to convert stateless legacy component with the newer style.
I have this
const Main = React.createClass({
render(){
return(<h1>my title</h1>)
}
})
so what's the 'newer' syntax for above code?
is it like this?
const Main = () => {
render(){
return(<h1>my title</h1>)
}
}
Then when to use React.component?
class Main extends React.Components() {
render(){ return(<h1>something</h1>}
}
There is not performance difference between using
const Main = () => {
return(<h1>my title</h1>)
}
or
class Main extends React.Component {
render(){ return(<h1>something</h1>}
}
given they receive the same props, however the react creators suggest that performance improvements will be made in future. So in short,
Use the first approach, which is a functional component approach when your component only takes in props and renders the result. However use the ES6 class based approach, when your React component has more functionality and handles states.
In your case you are just rendering the component so a functional component approach will be best suited for you
Both of your example is correct. Just that the extends Component should be without s and need to import React, { Component } from 'react'; at the top.
const Main = () => {
return(<h1>my title</h1>)
}
You generally want to use the above example when it is does not need any lifecycle methods. This are refer to as Pure component that will always behave the same when given the same props. It also does not keep state.
class Main extends React.Component() {
componentWillMount() {
console.log("I can be not pure");
}
render(){ return(<h1>something</h1>}
}
The above are Component that could manage state and use lifecycle methods.
Usually to decide what to use, I always start with a pure component. When continue developing, I discover that I have to use state or lifecycle methods, I would then turn it to a component.

Categories

Resources