Skip to main content

Class Components: A Look at the Past (Part 2) #15

📖 Introduction

In Part 1, we explored the basics of class components, including state and props. Now, we will delve into one of the most powerful features of class components: lifecycle methods. Understanding the component lifecycle is key to managing side effects, such as fetching data or setting up subscriptions.


📚 Prerequisites

Before we begin, please ensure you have a solid grasp of the following concepts:

  • The basic syntax of a React class component.
  • How to manage state with this.state and this.setState.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • Foundational Theory: The three phases of a component's lifecycle: Mounting, Updating, and Unmounting.
  • Core Implementation: The most common lifecycle methods: componentDidMount, componentDidUpdate, and componentWillUnmount.
  • Practical Application: How to fetch data from an API when a component mounts.
  • Advanced Techniques: How to perform actions when a component updates and how to clean up side effects.
  • Best Practices & Anti-Patterns: The traditional way of handling side effects in React.

🧠 Section 1: The Core Concepts of the Component Lifecycle

Every React component goes through a lifecycle of events. You can think of it as the component being "born," "living," and "dying." React provides special methods, called lifecycle methods, that allow us to hook into these phases and run code at specific times.

The three main phases are:

  1. Mounting: When an instance of a component is being created and inserted into the DOM.
  2. Updating: When a component is being re-rendered as a result of changes to either its props or state.
  3. Unmounting: When a component is being removed from the DOM.

💻 Section 2: Deep Dive - Common Lifecycle Methods

Let's explore the most important lifecycle methods you'll encounter.

2.1 - componentDidMount()

This method is called once, immediately after the component is rendered to the DOM for the first time. It's the perfect place to perform tasks that require a DOM node, like fetching data from an API or setting up a subscription.

// components/DataFetcher.jsx

import React, { Component } from 'react';

class DataFetcher extends Component {
constructor(props) {
super(props);
this.state = { data: null, loading: true };
}

componentDidMount() {
console.log('Component has mounted!');
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => this.setState({ data, loading: false }));
}

render() {
if (this.state.loading) {
return <div>Loading...</div>;
}
return <div>Data: {JSON.stringify(this.state.data)}</div>;
}
}

export default DataFetcher;

Walkthrough:

  • In componentDidMount, we make a network request to fetch data.
  • Once the data arrives, we call this.setState to update our component's state. This triggers a re-render to display the fetched data.

2.2 - componentDidUpdate()

This method is called immediately after updating occurs. It is not called for the initial render. You can use it to operate on the DOM when the component has been updated, or to perform network requests as long as you compare the current props to previous props (e.g., a network request may not be necessary if the props have not changed).

// components/Logger.jsx

import React, { Component } from 'react';

class Logger extends Component {
componentDidUpdate(prevProps, prevState) {
console.log('Component has updated!');
console.log('Previous props:', prevProps);
console.log('Current props:', this.props);
}

render() {
return <div>My prop value is: {this.props.value}</div>;
}
}

export default Logger;

Walkthrough:

  • componentDidUpdate receives the previous props and state as arguments.
  • This allows you to compare the current props and state to the previous ones and perform actions only when specific changes have occurred. This is crucial to prevent infinite loops.

2.3 - componentWillUnmount()

This method is called immediately before a component is unmounted and destroyed. It's the perfect place to perform any necessary cleanup, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that were created in componentDidMount.

// components/Timer.jsx

import React, { Component } from 'react';

class Timer extends Component {
constructor(props) {
super(props);
this.state = { time: new Date() };
}

componentDidMount() {
this.timerId = setInterval(() => {
this.setState({ time: new Date() });
}, 1000);
}

componentWillUnmount() {
console.log('Clearing the timer!');
clearInterval(this.timerId);
}

render() {
return <div>Current time: {this.state.time.toLocaleTimeString()}</div>;
}
}

export default Timer;

Walkthrough:

  • In componentDidMount, we set up an interval that updates the state every second. We store the interval ID on this.timerId.
  • In componentWillUnmount, we use clearInterval to clean up the timer. This prevents memory leaks and errors that would occur if setState were called on an unmounted component.

💡 Conclusion & Key Takeaways

Understanding the component lifecycle is essential for building robust and efficient React applications with class components. These methods give you precise control over what happens at each stage of a component's life.

Let's summarize the key takeaways:

  • Mounting, Updating, Unmounting: These are the three main phases of a component's lifecycle.
  • componentDidMount for Setup: Use this for initial data fetching and subscriptions.
  • componentDidUpdate for Changes: Use this to react to changes in props or state.
  • componentWillUnmount for Cleanup: Always clean up what you set up.

Challenge Yourself: Create a class component that fetches a new random user from the Random User API every time a button is clicked. Use componentDidMount for the initial fetch and a class method for subsequent fetches.


➡️ Next Steps

This concludes our look at the "old world" of class components. You now have the foundational knowledge to understand and work with legacy React code. In the next series, "Writing Markup with JSX", we will return to the modern world of functional components and take a much deeper dive into the power and nuances of JSX.

Thank you for your dedication. Stay curious, and happy coding!


glossary

  • Lifecycle Methods: Special methods in class components that are automatically called at different points in a component's life.
  • Mounting: The process of creating a component and inserting it into the DOM.
  • Updating: The process of re-rendering a component due to changes in props or state.
  • Unmounting: The process of removing a component from the DOM.
  • Side Effect: Any work that a component does other than rendering, such as data fetching, subscriptions, or manually changing the DOM.

Further Reading