Հայերեն Русский

Lesson 18: React Lifecycle Methods and Hooks (useState, useEffect)


Understanding Lifecycle Methods in Class Components

Introduction to Lifecycle Methods

In React, each component goes through a series of "lifecycle" events as it mounts, updates, and eventually unmounts. Lifecycle methods are specialized functions in class components that allow you to hook into these phases to perform specific operations. Understanding these methods is crucial for effective React development, especially when dealing with dynamic applications that require interaction with external systems like APIs or JavaScript frameworks.

Lifecycle Phases:

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

Key Lifecycle Methods

  1. componentDidMount:

    • Purpose: This method is called immediately after the component is mounted (inserted into the tree). It is useful for initializing the component, such as fetching data, adding event listeners, or interacting with the DOM when the component is first rendered.
    • Common Uses: Making AJAX calls to load data from a server.
    • Example:
      componentDidMount() {
          fetch('https://api.example.com/items')
              .then(response => response.json())
              .then(data => this.setState({ data }));
      }
      
  2. componentDidUpdate:

    • Purpose: This method is invoked immediately after updating occurs. This method is not called for the initial render.
    • Use Case: It is particularly useful for performing checks and executing functions after the component has updated in response to prop or state changes.
    • Example:
      componentDidUpdate(prevProps, prevState) {
          if (this.props.userID !== prevProps.userID) {
              this.fetchData(this.props.userID);
          }
      }
      
  3. componentWillUnmount:

    • Purpose: This method is invoked immediately before a component is unmounted and destroyed. It is useful for performing any necessary cleanup, such as invalidating timers, canceling network requests, or cleaning up subscriptions that were created in componentDidMount.
    • Example:
      componentWillUnmount() {
          clearInterval(this.interval);
      }
      

Practical Exercise: Fetch and Display User Data

Objective: Create a class component that interacts with an API to fetch user data and implements cleanup logic.

Steps to Implement:

  1. Setup a New React Class Component:

    • Create a new class component called UserProfile.
    • Initialize state to store user data.
  2. Implement componentDidMount to Fetch Data:

    • Use componentDidMount to fetch user data from a placeholder API and store it in the state.
    class UserProfile extends React.Component {
        constructor(props) {
            super(props);
            this.state = { userData: null };
        }
    
        componentDidMount() {
            fetch('https://api.example.com/user/' + this.props.userID)
                .then(response => response.json())
                .then(data => this.setState({ userData: data }));
        }
    
        componentDidUpdate(prevProps) {
            if (this.props.userID !== prevProps.userID) {
                this.fetchData();
            }
        }
    
        componentWillUnmount() {
            console.log('Cleaning up...');
        }
    
        render() {
            const { userData } = this.state;
            return (
                <div>
                    {userData ? (
                        <div>
                            <h1>{userData.name}</h1>
                            <p>{userData.email}</p>
                        </div>
                    ) : (
                        <p>Loading...</p>
                    )}
                </div>
            );
        }
    }
    
  3. Testing and Debugging:

    • Ensure the component correctly fetches and displays user data upon mounting.
    • Check that it appropriately cleans up resources or subscriptions when unmounted.

Exercise Deliverables:

This exercise will help students grasp the importance of lifecycle methods in managing external data and resources in a React application, providing them with practical skills to handle real-world scenarios involving dynamic data.

Transitioning to Hooks in Functional Components

Introduction to Hooks

Hooks were introduced in React 16.8 as a new addition that allows functional components to manage state and side effects, which were capabilities previously only possible in class components. This significant enhancement not only simplifies the React codebase by reducing the need for classes but also opens up more possibilities for code reuse and better encapsulation.

Advantages of Hooks:

Using the useState Hook

Basics of useState:

Managing State with useState:

Hands-on Exercise: Refactoring a Class Component

Objective: Refactor a class component that fetches user data into a functional component using useState.

Steps:

  1. Identify the State and Lifecycle Methods in the Class Component: Review the existing class component to identify how state and lifecycle methods like componentDidMount are being used.

  2. Convert to Functional Component Using useState:

    import React, { useState, useEffect } from 'react';
    
    function UserProfile({ userID }) {
        const [user, setUser] = useState(null);
    
        useEffect(() => {
            fetch(`https://api.example.com/user/${userID}`)
                .then(response => response.json())
                .then(data => setUser(data));
        }, [userID]); // Dependency array includes userID to refetch when it changes
    
        return (
            <div>
                {user ? (
                    <div>
                        <h1>{user.name}</h1>
                        <p>{user.email}</p>
                    </div>
                ) : (
                    <p>Loading...</p>
                )}
            </div>
        );
    }
    
  3. Test the Functional Component: Ensure the refactored component functions as expected, particularly that it correctly fetches and displays user data.

This exercise will solidify students' understanding of how to use useState in real-world scenarios and transition from class components to functional components, leveraging the power and simplicity of Hooks.

Handling Side Effects with useEffect

Understanding useEffect

The useEffect hook in React is essential for managing side effects in functional components. It is a powerful feature that replaces several lifecycle methods such as componentDidMount, componentDidUpdate, and componentWillUnmount from class components. By using useEffect, developers can organize all side effect logic into a single consistent API, making the code easier to maintain and understand.

What are Side Effects? Side effects are operations that affect other components, can't be done during rendering, and include:

Syntax and Usage

Basic Usage: useEffect allows you to perform side effects in your component. It takes two arguments:

  1. A function that contains the code for the side effect.
  2. An optional dependency array that dictates when the effect should rerun.
import { useEffect } from 'react';

function ExampleComponent() {
    useEffect(() => {
        // Code here will run after every render by default
        console.log('Component rendered or updated');

        // Optional cleanup mechanism
        return () => {
            console.log('Cleanup on component unmount or before next re-render');
        };
    });
}

Dependency Array: The dependency array is a crucial aspect of useEffect that controls its execution:

Example of Dependency Array Usage:

function UserComponent({ userID }) {
    const [user, setUser] = useState(null);

    useEffect(() => {
        fetch(`https://api.example.com/users/${userID}`)
            .then(response => response.json())
            .then(userData => setUser(userData));

        return () => {
            // Cleanup code here
        };
    }, [userID]); // Effect re-runs only if userID changes
}

Complex Effects and Cleanup

Returning a Cleanup Function: useEffect may optionally return a function that cleans up after the effect. This is useful for:

Example with Cleanup:

function TimerComponent() {
    useEffect(() => {
        const timerID = setInterval(() => {
            console.log('Timer tick');
        }, 1000);

        return () => {
            clearInterval(timerID);
        };
    }, []); // Empty dependency array means this runs only on mount
}

Practical Exercises

Exercise 1: Data Fetching Effect

Exercise 2: Interval Timer Effect

Implementation of Clock Component:

function Clock() {
    const [currentTime, setCurrentTime] = useState(new Date());

    useEffect(() => {
        const timerId = setInterval(() => {
            setCurrentTime(new Date());
        }, 1000);

        return () => {
            clearInterval(timerId);
        };
    }, []); // Empty array ensures this runs only once

    return (
        <div>
            <h1>Current Time</h1>
            <p>{currentTime.toLocaleTimeString()}</p>
        </div>
    );
}

These exercises are designed to provide hands-on experience with useEffect, demonstrating how it can be used to manage side effects efficiently in functional components. The exercises highlight both basic and advanced usage, including the critical role of the cleanup function in avoiding resource leaks.

Wrap-up and Q&A

As we conclude today's detailed exploration of React's lifecycle methods in class components and the revolutionary Hooks in functional components, let's recap the key concepts and prepare for a Q&A session to clear up any remaining uncertainties.

Review and Summary

Lifecycle Methods in Class Components:

Introduction to Hooks:

Practical Applications and Exercises:

Q&A Session

Now, let's transition into our Q&A session. This is an excellent opportunity for you to ask questions about any aspect of today's material. Whether you need clarification on the details of lifecycle methods, the mechanics of specific Hooks, or advice on how to apply these concepts to real-world problems, feel free to bring your queries forward.

Possible Topics for Discussion:

Feel free to share your experiences, any specific challenges you've faced, or how you envision applying what you've learned in your projects. This discussion can help solidify your understanding and inspire new ideas for leveraging React's powerful features in your development work.