Swiftorial Logo
Home
Swift Lessons
Tutorials
Learn More
Career
Resources

React FAQ: Top 75 Questions

6. What is a React Component Lifecycle?

The **React Component Lifecycle** refers to the series of methods that are invoked at different stages of a component's existence. These stages include when a component is created, updated, and destroyed (mounted, updated, unmounted). Understanding the lifecycle methods allows developers to perform actions at specific times, such as fetching data, manipulating the DOM, or setting up/tearing down subscriptions.

While class components use explicit lifecycle methods (e.g., `componentDidMount`, `componentDidUpdate`), functional components achieve similar lifecycle behaviors using **Hooks** (primarily `useEffect`).

The main phases of a component's lifecycle are:

  • Mounting: The component is being created and inserted into the DOM.
    • Functional: `useEffect` with an empty dependency array (`[]`).
    • Class: `constructor()`, `static getDerivedStateFromProps()`, `render()`, `componentDidMount()`.
  • Updating: The component is being re-rendered as a result of changes to props or state.
    • Functional: `useEffect` with dependencies.
    • Class: `static getDerivedStateFromProps()`, `shouldComponentUpdate()`, `render()`, `getSnapshotBeforeUpdate()`, `componentDidUpdate()`.
  • Unmounting: The component is being removed from the DOM.
    • Functional: `useEffect` cleanup function (return a function from `useEffect`).
    • Class: `componentWillUnmount()`.
  • Error Handling (Class Components only):
    • `static getDerivedStateFromError()`
    • `componentDidCatch()`

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom/client';

// Functional Component demonstrating lifecycle with useEffect
function LifecycleDemoFunctional() {
  const [count, setCount] = useState(0);

  // Mounting Phase (runs once after initial render)
  useEffect(() => {
    console.log('Functional: Component Mounted!');
    // This is equivalent to componentDidMount

    // Optional: Unmounting Phase (cleanup function)
    return () => {
      console.log('Functional: Component Unmounted (cleanup)!');
      // This is equivalent to componentWillUnmount
    };
  }, []); // Empty dependency array means it runs only on mount/unmount

  // Updating Phase (runs on initial render and whenever count changes)
  useEffect(() => {
    console.log(`Functional: Count Updated to ${count}`);
    // This is equivalent to componentDidMount + componentDidUpdate for 'count'
  }, [count]); // Dependency array includes 'count'

  return (
    <div>
      <h3>Functional Component Lifecycle</h3>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
    </div>
  );
}

// Class Component demonstrating lifecycle methods
class LifecycleDemoClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: 'Hello'
    };
    console.log('Class: Constructor called');
  }

  static getDerivedStateFromProps(props, state) {
    console.log('Class: getDerivedStateFromProps called');
    return null; // No state update from props in this example
  }

  componentDidMount() {
    console.log('Class: componentDidMount called!');
    // Best place for data fetching, subscriptions
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log('Class: shouldComponentUpdate called');
    return true; // Always re-render
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('Class: getSnapshotBeforeUpdate called');
    return null; // No snapshot data in this example
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('Class: componentDidUpdate called!');
    // Called after re-render, good for DOM manipulation or network requests based on state/prop changes
  }

  componentWillUnmount() {
    console.log('Class: componentWillUnmount called!');
    // Perform cleanup here (e.g., clear timers, cancel network requests)
  }

  render() {
    console.log('Class: Render called');
    return (
      <div>
        <h3>Class Component Lifecycle</h3>
        <p>Message: {this.state.message}</p>
        <button onClick={() => this.setState({ message: 'Updated ' + Math.random().toFixed(2) })}>
          Update Message
        </button>
      </div>
    );
  }
}

// Parent component to mount/unmount the demos
function App() {
  const [showFunctional, setShowFunctional] = useState(true);
  const [showClass, setShowClass] = useState(true);

  return (
    <div>
      <button onClick={() => setShowFunctional(!showFunctional)}>
        Toggle Functional Demo
      </button>
      <button onClick={() => setShowClass(!showClass)}>
        Toggle Class Demo
      </button>
      <hr />
      {showFunctional && <LifecycleDemoFunctional />}
      {showClass && <LifecycleDemoClass />}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root-lifecycle'));
root.render(<App />);
                    

Explanation of the Code:

  • `LifecycleDemoFunctional` (Functional Component):
    • The first `useEffect` with `[]` (empty dependency array) simulates `componentDidMount` (for setup) and `componentWillUnmount` (for cleanup via its return function). It runs once after the initial render and its cleanup runs when the component is removed.
    • The second `useEffect` with `[count]` simulates `componentDidMount` + `componentDidUpdate` for changes related to the `count` state. It runs on initial render and every time `count` changes.
  • `LifecycleDemoClass` (Class Component):
    • `constructor`: Initializes state and binds methods. Runs first.
    • `static getDerivedStateFromProps`: Syncs state with props.
    • `render`: Renders the JSX. Called frequently.
    • `componentDidMount`: Called *after* the component is mounted to the DOM. Ideal for API calls, setting up subscriptions.
    • `shouldComponentUpdate`: Optimizes performance by preventing unnecessary re-renders (return `false`).
    • `getSnapshotBeforeUpdate`: Captures information from the DOM just before updates are applied.
    • `componentDidUpdate`: Called *after* component updates are flushed to the DOM. Good for side effects that need DOM access or reacting to state/prop changes.
    • `componentWillUnmount`: Called just before component unmounts. Ideal for cleanup (e.g., clearing timers, canceling network requests).
  • The `App` component acts as a controller to mount and unmount `LifecycleDemoFunctional` and `LifecycleDemoClass`, allowing you to observe their respective console logs during different lifecycle phases.

This example demonstrates how to perform side effects and manage component behavior at different stages of their lifecycle, either through React Hooks in functional components or dedicated methods in class components.