React/ReactJS: Difference between componentWillMount() and componentDidMount()

The comparison between componentWillMount() and componentDidMount() methods would have made much more sense prior to React 16.3, when componentWillMount() was listed as any other lifecycle method and their mounting order were as follows:

But since v. 16.3, the componentWillMount() method has been marked for removal in future versions, but at the same time, an alias UNSAFE_componentWillMount() was introduced. Check changelog.

reactjs 16.3.0

As per the new revised official React document here, the mounting order has been reinstated as follows:

The legacy method componentWillMount() is heading for deprecation and will not be available from v. 17. But for the time being, it is available under the alias UNSAFE_componentWillMount().

componentWillMount()

componentWillMount() is mounted before the render() method and is essentially a constructor, akin to the ES6 constructor() method. This is where you set properties and states (using setState()) and pull synchronous data. If the application is rendered on the server (with the likes of Next.js and such) it is invoked once on the server side and also on the client side.

But it is not exactly a constructor, as you can setState() inside componentWillMount() but not inside a constructor(). If you try to setState() inside a constructor(), it will throw the following warning message.

						
							Can't call setState on a component that is not yet mounted.
						
					

But the setState() inside componentWillMount() will not rerender the component (unless it is inside some async methods like fetch() or axios()).

Since it is loaded before render(), there is quite a misconception that componentWillMount() is the right place to fetch data to avoid initial empty rendering of states, so that around the time when the elements render, the data would already be available. However, this is not the case as render() is called immediately after componentWillMount(); it does not wait for the availability of the data being fetched by the async method inside it.

We will illustrate this fallacy with an example below. We will fetch data inside componentWillMount() from an external source, the free online REST API for fake data: https://jsonplaceholder.typicode.com/users

						
							class Users extends React.Component{
							  constructor(props) {
							    super(props);
							    this.state = {
							      users: []
							    }
							  }

							  componentWillMount() {
							     fetch('https://jsonplaceholder.typicode.com/users')
							      .then(res => res.json())
							      .then(users => this.setState({ users }));
							  }

							  render() {
							    return (
							      <div>
							      	{this.state.users.length == 0?
							      	<p>EMPTY</p>: 
							        <ul>
							          {
							            this.state.users.map(usr =>
							              <li key={usr.id}>{usr.name}</li>
							            )
							          }
							        </ul>}
							      </div>
							    );
							  }
							}

							export default Users;
						
					
reactjs fetch componentwillmount

If you observe carefully, you will notice the "EMPTY" text flashing very quickly on first load (meeting the condition this.state.users.length == 0), before loading the names of the people listed in the JSON next. That was indication that the render() method was called irrespective of the status of the async call inside componentWillMount(). But this fetched async data cannot be used in server-side rendering, as it is necessary to provide synchronous data for it. But providing synchronous data can be achieved inside a constructor() also. Besides, the component's DOM has not yet been materialized to do anything and nothing much has happened since the load of constructor(). In short, there is not much need of this method. That is why, currently, it is advised not to use componentWillMount() anymore; the ES6 constructor() method is recommended to be used in its place for initializing state. And for fetching remote data, the other lifecycle method componentDidMount() is recommended.

NOTE: The componentWillMount() method is now considered a legacy. It is advised not to use this method in your code anymore.

componentDidMount()

This method is called just after render(). And since the render() function is called before componentDidMount(), the component has already been rendered once by the time it invokes componentDidMount(). Also, unlike the earlier componentWillMount() method, this method is not called on the server-side, but only on the client-side. This is the right place for API calls. Besides API calls, this is where you initialize third-party DOM libraries and add event listeners.

The example code in the previous section can now be corrected by fetching external data inside componentDidMount() as shown below.

						
							class Users extends React.Component{
							  constructor(props) {
							    super(props);
							    this.state = {
							      users: []
							    }
							  }

							  componentDidMount() {
							     fetch('https://jsonplaceholder.typicode.com/users')
							      .then(res => res.json())
							      .then(users => this.setState({ users }));
							  }

							  render() {
							    return (
							      <div>
							      	{this.state.users.length == 0?
							      	<p>EMPTY</p>: 
							        <ul>
							          {
							            this.state.users.map(usr =>
							              <li key={usr.id}>{usr.name}</li>
							            )
							          }
							        </ul>}
							      </div>
							    );
							  }
							}

							export default Users;