React Lifecycle Methods

Khushboo Tolat Modi
12 min read4 days ago

--

In React, lifecycle methods are special methods that get called at different stages of a component’s existence. They allow you to control what happens to a component at various stages like mounting, updating, or unmounting. With the introduction of React Hooks in React 16.8, functional components can also manage their own side effects, but lifecycle methods are still important in class components. Here’s a detailed look at the most commonly used lifecycle methods:

Mounting

The mounting phase in React refers to the process where a component is created and inserted into the DOM. This phase involves a series of lifecycle methods called in a specific order, allowing developers to initialize and configure the component before it is rendered. Here’s a detailed breakdown of each method in the mounting phase in order of their execution:

1. `constructor(props)`

Purpose:

  • The `constructor` is the first method called when a component instance is created. It is used to initialize the component’s state and bind event handlers.
  • In the `constructor`, you can set the initial state by directly assigning an object to `this.state`. You also typically pass `props` to the base `Component` class using `super(props)` to ensure the component is properly initialized.

Example:

Notes:

  • The `constructor` is only called once during the component’s lifecycle.
  • You should avoid side effects in the `constructor` (e.g., network requests or subscriptions) and reserve those tasks for `componentDidMount`.

2. `static getDerivedStateFromProps(props, state)`

Purpose:

  • This is a static method that is called right before rendering, both during the initial mount and during updates. It allows the component to update its state based on changes in props.
  • It returns an object to update the state or `null` if no state updates are needed.

Example:

Notes:

  • This method is rarely needed, as React’s data flow is usually handled by passing props directly.
  • It’s used in special cases where state needs to be derived from props.

3. `render()`

Purpose:

  • The `render` method is the only required lifecycle method in a class component. It determines what the UI of the component should look like by returning React elements.
  • This method is pure, meaning it should not modify the component state or interact with the DOM.

Example:

Notes:

  • The `render` method can return a variety of values, including JSX elements, arrays, fragments, portals, strings, numbers, or `null`.
  • Since `render` is pure, avoid side effects or state mutations within this method.

4. `componentDidMount()`

Purpose:

  • This method is invoked immediately after a component is mounted (i.e., inserted into the DOM). It’s the perfect place to run side effects, such as fetching data from an API, setting up subscriptions, or initializing third-party libraries.
  • `componentDidMount` is the last method called in the mounting phase, making it ideal for any DOM manipulations.

Example:

Notes:

  • Since `componentDidMount` is called after the initial render, updating the state within it will trigger a re-render. This is common when fetching data or interacting with the DOM.
  • If you set up subscriptions or event listeners here, remember to clean them up in `componentWillUnmount` to avoid memory leaks.

Updating

The updating phase in React refers to the process when a component is re-rendered due to changes in its state or props. During this phase, several lifecycle methods are invoked in a specific order, allowing you to control how your component reacts to these changes. Here’s a detailed look at each method involved in the updating phase in order of their execution:

1. `static getDerivedStateFromProps(props, state)`

Purpose:

  • This static method is called right before rendering the component when new props or state are received. It allows the component to update its internal state based on changes in props.
  • It returns an object that updates the state or `null` if no updates are needed.

Example:

Notes:

  • This method is useful in scenarios where the state needs to be synchronized with props.
  • It is called on every update, so avoid heavy computations here.

2. `shouldComponentUpdate(nextProps, nextState)`

Purpose:

  • This method is called before rendering when new props or state are received. It allows you to control whether the component should update or not. Returning `true` (default) means the component will update; returning `false` means it will not.
  • It is mainly used to optimize performance by preventing unnecessary re-renders.

Example:

Notes:

  • This method is not called during the initial render or when `forceUpdate()` is used.
  • Avoid complex logic here, as it can lead to performance issues or bugs if not handled carefully.

3. `render()`

Purpose:

  • The `render` method is called to produce the next version of the virtual DOM based on the component’s current state and props.
  • It’s pure, meaning it should not modify component state or interact with the DOM.

Example:

Notes:

  • Since `render` is pure, any state or prop changes should be reflected in the returned JSX.
  • Avoid side effects (like modifying the DOM directly or making network requests) within `render`.

4. `getSnapshotBeforeUpdate(prevProps, prevState)`

Purpose:

  • This method is called right before the changes from the virtual DOM are actually reflected in the real DOM. It allows you to capture some information (like the current scroll position) before it is potentially changed.
  • The value returned from this method is passed as a third argument to `componentDidUpdate`.

Example:

Notes:

  • This method is particularly useful for capturing information about the DOM before it changes, such as maintaining scroll position during updates.
  • It is often used together with `componentDidUpdate`.

5. `componentDidUpdate(prevProps, prevState, snapshot)`

Purpose:

  • This method is invoked immediately after updating occurs. It’s a good place to perform any DOM operations, network requests, or other side effects based on the update.
  • It receives the previous props and state, as well as the value returned by `getSnapshotBeforeUpdate` (if any).

Example:

Notes:

  • This method is useful for performing operations that need to happen after the DOM has been updated.
  • Avoid setting state within `componentDidUpdate` unless it’s wrapped in a condition to prevent an infinite loop of updates.

Unmounting

The unmounting phase in React occurs when a component is being removed from the DOM. This phase has a single lifecycle method that allows you to perform any necessary cleanup tasks before the component is destroyed. Proper handling of this phase is crucial to prevent memory leaks, dangling event listeners, or other side effects that could persist after the component is removed.

1. `componentWillUnmount()`

Purpose:

componentWillUnmount is invoked immediately before a component is unmounted and destroyed. This method is used for cleanup activities, such as:

  • Canceling network requests.
  • Clearing timers or intervals.
  • Removing event listeners.
  • Cleaning up subscriptions (e.g., from Redux, WebSockets, etc.).
  • Invalidating or cleaning up any side effects created in `componentDidMount` or other lifecycle methods.

Example:

In this example:

  • A timer is started when the component mounts (`componentDidMount`).
  • The timer is cleared in `componentWillUnmount` to ensure it doesn’t continue running after the component is removed from the DOM. This is crucial to prevent potential memory leaks or unexpected behavior.

Key Considerations:

  • Preventing Memory Leaks: If you set up event listeners or intervals in `componentDidMount`, you must remove them in `componentWillUnmount` to prevent memory leaks. Failing to do so could lead to your application consuming more memory over time or behaving unexpectedly.
  • Cleaning Up Subscriptions: If your component subscribes to external data sources (like Redux stores, Firebase, WebSocket connections, etc.), you should unsubscribe in `componentWillUnmount`. This ensures that your component no longer reacts to updates from those sources after it has been removed.
  • No `setState`: Since the component is about to be destroyed, you should not call `setState` within `componentWillUnmount`. Doing so will have no effect because the component will not re-render.
  • Asynchronous Cleanup: If your cleanup involves asynchronous operations (like canceling a network request), ensure that those operations are properly handled to avoid race conditions or trying to interact with a component that no longer exists.

Common Use Cases:

  • Timers and Intervals: Clearing `setTimeout` or `setInterval` instances to avoid them running after the component is unmounted.
  • Event Listeners: Removing event listeners attached to the window, document, or any DOM element to prevent them from firing after the component is unmounted.
  • Subscriptions: Unsubscribing from data streams or external services (e.g., WebSockets, Firebase, Redux stores).
  • Network Requests: Cancelling ongoing network requests if the component is unmounted before the request completes. This can be done using libraries like Axios, which provide cancellation tokens.

Best Practices:

  • Always clean up side effects in `componentWillUnmount` if they were set up in `componentDidMount` or any other lifecycle method.
  • Be mindful of asynchronous operations to ensure they don’t inadvertently interact with a component that has been unmounted.
  • Avoid any logic that assumes the component will continue to exist after `componentWillUnmount` is called.

Error Handling

The error handling phase in React is designed to catch and handle errors that occur during rendering, in lifecycle methods, and in constructors of the whole tree below a component. This is accomplished using special lifecycle methods in class components known as error boundaries.

Error Boundaries Overview

  • Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of crashing the entire application. This makes the app more resilient by preventing errors from propagating to the root of the application.

1. `static getDerivedStateFromError(error)`

Purpose:

  • This static method is called when an error is thrown during the rendering phase, in a lifecycle method, or in a constructor of any child component.
  • It allows you to update the state so that the next render will show a fallback UI.

Usage:

  • The method receives the error that was thrown as a parameter and returns an object that updates the component’s state.
  • By setting the state in this method, you can render a fallback UI that informs the user something went wrong.

Example:

Notes:

  • This method allows you to control what is rendered when an error occurs. For example, you might choose to render a generic error message or a custom error component.
  • It’s typically used to set an error state that can trigger the rendering of a fallback UI.

2. `componentDidCatch(error, info)`

Purpose:

  • This method is called after an error has been thrown by a descendant component. It’s used for logging error information or performing side effects like reporting the error to an error tracking service.
  • Unlike `getDerivedStateFromError`, this method can be used to capture additional details about the error and the component stack trace where the error occurred.

Usage:

The method receives two arguments:

  • `error`: The error that was thrown.
  • `info`: An object with a `componentStack` property that contains a string with information about which component threw the error.

Example:

Notes:

  • `componentDidCatch` is particularly useful for logging errors or sending them to a monitoring service (e.g., Sentry, LogRocket).
  • While `getDerivedStateFromError` helps with rendering a fallback UI, `componentDidCatch` focuses on capturing and logging error details.

How to Use Error Boundaries

  • Wrapping Components: Error boundaries can be used to wrap any component or set of components. This can be done globally (e.g., around the entire application) or more selectively (e.g., around components that are more prone to errors).

Example:

In this example, ErrorBoundary wraps MyComponent. If MyComponent or any of its children throw an error, the ErrorBoundary will catch it and display the fallback UI.

Key Considerations:

Error Boundaries Catch Errors in the Following Scenarios:

  • During rendering.
  • In lifecycle methods (including those in class components).
  • In constructors of child components.

Error Boundaries Do Not Catch Errors in the Following Scenarios:

  • Event handlers (errors can be caught using try/catch blocks within the event handler itself).
  • Asynchronous code (e.g., `setTimeout`, `requestAnimationFrame`).
  • Server-side rendering.
  • Errors thrown in the error boundary itself (though you can nest error boundaries to catch such errors).

Best Practices:

  • Use error boundaries to prevent the entire app from crashing due to small, isolated errors.
  • Place error boundaries around potentially error-prone parts of your app, such as third-party components or components handling complex logic.
  • Ensure that error boundaries provide a user-friendly fallback UI, informing the user that something went wrong.

Understanding these lifecycle methods will help you better manage state, props, and side effects in React components.

Lifecycle methods in Functional Component

In functional components, React’s `useEffect` hook is the primary way to handle side effects and lifecycle methods. The `useEffect` hook can replicate behaviors similar to class component lifecycle methods like `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount`. Here’s a detailed breakdown of how `useEffect` works and how it relates to these lifecycle methods.

Basic Syntax

  • First Argument (`effect`): A function where you place your side effect logic. This function can return a cleanup function to clean up resources when the component unmounts or before the effect is re-run.
  • Second Argument (`dependencies`): An array of dependencies that determine when the effect should be re-run. If any of the values in this array change, the effect is triggered again.

`useEffect` as `componentDidMount`

To replicate the behavior of `componentDidMount` (which runs once after the component is mounted), you can use `useEffect` with an empty dependency array `[]`.

Example:

  • Behavior: The effect runs only once after the initial render, similar to `componentDidMount` in class components.

`useEffect` as `componentDidUpdate`

To mimic `componentDidUpdate`, you use `useEffect` with dependencies. The effect will run whenever any of the dependencies change.

Example:

  • Behavior: The effect runs after each render where the dependencies (`count` or `someProp`) change, just like `componentDidUpdate`.

`useEffect` as `componentWillUnmount`

To replicate `componentWillUnmount`, you return a cleanup function from `useEffect`. This cleanup function will be executed when the component unmounts or before the effect re-runs.

Example:

  • Behavior: The cleanup function runs when the component is about to unmount, similar to `componentWillUnmount`.

Handling All Three Lifecycle Methods in One `useEffect`

In many cases, a single `useEffect` can handle the component’s mounting, updating, and unmounting phases. Here’s an example that demonstrates this:

Example:

  • Mounting: The effect runs once on the initial render.
  • Updating: The effect runs whenever `someProp` changes.
  • Unmounting: The cleanup function runs when the component unmounts or before the effect re-runs due to a dependency change.

Controlling the Execution Frequency of `useEffect`

The behavior of `useEffect` is controlled by the dependency array:

  • No Dependency Array: The effect runs after every render.
  • Empty Dependency Array `[]`: The effect runs only once, after the initial render (mimicking `componentDidMount`).
  • Specific Dependencies: The effect runs whenever any of the specified dependencies change.

Example: Without Dependency Array

  • Behavior: The effect runs after every render (initial and updates).

Common Pitfalls and Best Practices

  • Avoid Missing Dependencies: Always include all state and props that are used inside `useEffect` in the dependency array to avoid stale closures and bugs.
  • Multiple `useEffect` Calls: It’s common and recommended to use multiple `useEffect` hooks in a single component, each responsible for a specific side effect. This keeps the code more modular and easier to manage.
  • Cleanup: Always consider cleanup for effects that involve subscriptions, timers, or any other resource that should be released when the component unmounts or the effect is re-triggered.

Understanding and using `useEffect` effectively allows you to manage side effects in a clean, predictable way within functional components, providing the same capabilities that class components have with lifecycle methods.

--

--

Khushboo Tolat Modi
0 Followers

Frontend Developer with 3+ years of experience crafting progressive web solutions that enhance customer engagement.