RefactoringLifecycleMethods

Lifecycle Methods And useEffect

In older React applications (pre-hooks), class components relied on special "lifecycle methods" to handle tasks that happen at different stages of a component’s existence. These methods include:

Starting with React 16.8, Hooks introduced useEffect, a function-based approach to mimic these lifecycle behaviors in function components. By understanding how lifecycle methods map to useEffect, you can convert old class components into the more modern function-based approach.

Step By Step Example

Let's consider a class component that:

Folder And Filenames: Suppose you have a src/components folder with class_lifecycle_example.js. It might look like this:

import React from 'react';

class ClassLifecycleExample extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  componentDidMount() {
    setTimeout(() => {
      this.setState({ count: 5 });
    }, 5000);
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.count !== this.state.count) {
      console.log('hello world!');
    }
  }

  componentWillUnmount() {
    console.log('cleanup');
  }

  render() {
    const { count } = this.state;
    return (
      <>
        <div>{count}</div>
        <button onClick={() =>
          this.setState(state => ({ count: state.count + 1 }))
        }>
          Increment
        </button>
      </>
    );
  }
}

export default ClassLifecycleExample;
  

You now want to refactor it to a function-based component that uses useEffect.

Refactoring Steps / Follow Along:

Converted File: src/components/lifecycle_function_example.js

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

function LifecycleFunctionExample() {
  const [count, setCount] = useState(0);

  // Imitates componentDidMount
  useEffect(() => {
    // After 5 seconds, set count to 5
    const timerId = setTimeout(() => {
      setCount(5);
    }, 5000);

    // Imitates componentWillUnmount
    return () => {
      clearTimeout(timerId);
      console.log('cleanup');
    };
  }, []);

  // Imitates componentDidUpdate for count
  useEffect(() => {
    console.log('hello world!');
  }, [count]);

  return (
    <>
      <div>{count}</div>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>
        Increment
      </button>
    </>
  );
}

export default LifecycleFunctionExample;
  

Notice how we used two separate useEffect calls: one for the initial mount/unmount logic, and one for reacting to changes in count. This separation keeps your code organized, letting each useEffect handle a single responsibility.

Analogies And Metaphors

You might think of the component's lifecycle like a person's life events:

The useEffect hook is a scheduling system that neatly lines up these events in your function-based approach, ensuring you can add or remove effects as needed.

Real World Usage

This pattern commonly appears when you need to:

Code Explanations

In the example above:

Further Examples And Extra Information

With class components, you might have multiple pieces of logic jammed into componentDidMount or componentDidUpdate. With function components, you can separate them into multiple useEffect calls, making the code more maintainable and concise.

You might also consider:

What You Learned

In this reading, you discovered how to:

By mastering this mapping of lifecycle methods to useEffect, you can confidently migrate older class component code to modern function-based code, leading to more consistent and streamlined React applications.