React Lifecycle Methods and Their Equivalents in Functional Components

React Lifecycle Methods and Their Equivalents in Functional Components

React is the most popular JavaScript library for building user interfaces, and it provides a set of lifecycle methods that allow us to interact with our components at different stages

Lifecycle Phases

React components go through 3 phases during the lifecycle

  1. Mounting Phase - In the mounting phase, a component is being created and inserted into the DOM for the first time
  2. Update Phase - The update phase is triggered when a component's state or props change
  3. Unmount Phase - The unmount phase is triggered when a component is removed from the DOM

Class-based vs Functional Components

Class-based components were the traditional way of creating components in React. However, with the introduction of Hooks in React 16.8, each lifecycle method has an equivalent in functional components using React Hooks.

Let's create a class-based component called UserProfile and explore all of its lifecycle hooks and then we’ll see how we can use their equivalents in functional components.

import React, { Component } from 'react';

class UserProfile extends Component {
  
}

export default UserProfile;        

Mounting Phase

Let's start with the mounting phase. This is the phase where the component is created and added to the DOM. The following lifecycle methods are called during the mounting phase:

  • constructor(props) - This is the first method that's called when the component is created. It's the perfect place to set up your component's initial state and bind methods to the component.
  • render() - This method is called next and returns the JSX that represents the component in the DOM.
  • componentDidMount() - This method is called after the component has been added to the DOM. We use it to fetch data or set up event listeners.

import React, { Component } from 'react';

class UserProfile extends Component {
  constructor(props) {
    super(props);
    this.state = { user: props.user };
  }

  componentDidMount() {
    console.log('Component mounted');
  }

  render() {
    return (
      <div>
        <h2>{this.state.user.name}</h2>
        <p>{this.state.user.email}</p>
      </div>
    );
  }
}

export default UserProfile;        

Here's the equivalent functional component code for the mounting phase:

import React, { useState, useEffect } from 'react';

function UserProfile(props) {
  const [user, setUser] = useState(props.user);

  useEffect(() => {
    console.log('Component mounted');
  }, []);

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

export default UserProfile;        

In functional components, we don't have a constructor method. Instead, we can use the useState hook to initialize state values and perform any setup logic that we would normally do in the constructor.

The render() method is replaced by simply returning the JSX markup from the functional component.

We've used the useEffect hook to replace the componentDidMount method. We can pass an empty array [] as the second argument to useEffect to simulate componentDidMount.

Update Phase

Moving on to the update phase. This is the phase where the component's props or state change and it is re-rendered. The following lifecycle methods are called during the updating phase:

  • shouldComponentUpdate(nextProps, nextState) - This method is called before the component is re-rendered. We use it to determine if the component should update or not by returning a boolean value. shouldComponentUpdate is part of React.PureComponent.
  • render() - This method is called next and returns the JSX that represents the updated component in the DOM.
  • componentDidUpdate(prevProps, prevState) - This method is called after the component has been re-rendered. We use it to perform any side effects like updating DOM or fetching new data.

import React, { Component } from 'react';

class UserProfile extends Component {
  constructor(props) {
    super(props);
    this.state = { user: props.user };
  }

	shouldComponentUpdate(nextProps, nextState) {
    console.log('shouldComponentUpdate', nextProps, nextState);
    return true;
  }

  componentDidUpdate(prevProps, prevState) {
    console.log('Component updated');
  }

  render() {
    return (
      <div>
        <h2>{this.state.user.name}</h2>
        <p>{this.state.user.email}</p>
      </div>
    );
  }
}

export default UserProfile;        

Here's the equivalent functional component code for the update phase:

import React, { useState, useEffect } from 'react';

const UserProfile = (props) => {
  const [user, setUser] = useState(props.user);

  useEffect(() => {
    console.log('Component updated');
  });

  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

export default React.memo(UserProfile)        

We have used the useEffect hook without any dependencies, so it will be called after every render to replicate the behavior of the componentDidUpdate method.

However, it is more common to pass a specific dependency array here, because having a useEffect hook without dependencies can easily cause infinite re-renders

The equivalent of shouldComponentUpdate can be using React.memo?high-order component on export, which lets you skip re-rendering a component when its props are unchanged

Unmount Phase

The unmount phase is the final phase in React lifecycle, which is triggered when a component is removed from the DOM. This can happen when a component is removed due to a parent component being removed or when the component is conditionally rendered and its condition becomes false.

In this phase, the componentWillUnmount() method is called, which allows us to perform any necessary cleanup operations such as removing event listeners or canceling network requests.

import React, { Component } from 'react';

class UserProfile extends Component {
  constructor(props) {
    super(props);
    this.state = { user: props.user };
  }

	componentWillUnmount() {
    console.log('Component will unmount');
  }

  render() {
    return (
      <div>
        <h2>{this.state.user.name}</h2>
        <p>{this.state.user.email}</p>
      </div>
    );
  }
}

export default UserProfile;        

To achieve the same functionality in a functional component, we can use the useEffect() hook with a cleanup function as follows:

import React, { useEffect } from 'react';

function MyComponent() {
  const [user, setUser] = useState(props.user);

  useEffect(() => {
    return () => {
      console.log('Component unmounted');
    };
  }, []);

	return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

export default MyComponent;        

Instead of componentWillUnmount we specify a cleanup function that will be called when the component is unmounted by returning a function from the useEffect() hook.

Summary

You can see that the functional component looks a lot cleaner and more concise compared to the class-based one. A lot of boilerplate code is removed, making it easier to read and understand.

React now promotes the use of functional components as they offer a simpler and more concise way of writing components. All the lifecycle hooks are available in functional components as well.

It's worth mentioning that there are other lifecycle hooks, that we haven't discussed in this tutorial (such as getDerivedStateFromProps, getSnapshotBeforeUpdate, etc). These hooks are less commonly used but still have their place in specific scenarios.

#react #lifecyclemethods #reactlifecycle #lifecyclehook #reactlifecyclemethod

Rakib Hossain

Full Stack Web Developer

9 个月

Thank you

回复

要查看或添加评论,请登录

社区洞察

其他会员也浏览了