React Lifecycle Methods

Class components have lifecycle methods: special methods that React calls at different points during a component’s existence. This page explains what each method does and shows the equivalent approach using hooks, which is what you should use in modern React.

If you are writing new React code, you do not need to write lifecycle methods. Use hooks in functional components instead. This page is most useful if you are reading or maintaining older class-based code.

The three phases

A React component goes through three phases:

  1. Mounting: the component is created and added to the DOM for the first time.
  2. Updating: the component re-renders because state or props changed.
  3. Unmounting: the component is removed from the DOM.

Lifecycle methods let you run code at each of these moments.

Mounting: componentDidMount

componentDidMount runs after the component appears in the DOM for the first time. This is where you would fetch initial data, set up subscriptions, or start timers.

// Class component
class UserProfile extends React.Component {
  componentDidMount() {
    fetch('/api/user')
      .then((res) => res.json())
      .then((data) => this.setState({ user: data }));
  }

  render() {
    return <div>{this.state?.user?.name}</div>;
  }
}

Hook equivalent: useEffect with an empty dependency array runs once after the first render, which is the same timing as componentDidMount.

// Functional component with hooks
import { useState, useEffect } from 'react';

function UserProfile() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch('/api/user')
      .then((res) => res.json())
      .then((data) => setUser(data));
  }, []); // empty array means "run once on mount"

  return <div>{user?.name}</div>;
}

Updating: componentDidUpdate

componentDidUpdate runs after every re-render (except the first). It receives the previous props and state so you can compare them and decide whether to take action.

// Class component
class SearchResults extends React.Component {
  componentDidUpdate(prevProps) {
    if (prevProps.query !== this.props.query) {
      this.fetchResults(this.props.query);
    }
  }

  render() { /* ... */ }
}

Hook equivalent: useEffect with the relevant value in the dependency array. React re-runs the effect whenever that value changes.

// Functional component with hooks
function SearchResults({ query }) {
  const [results, setResults] = useState([]);

  useEffect(() => {
    fetchResults(query).then(setResults);
  }, [query]); // runs whenever `query` changes

  return <ul>{results.map((r) => <li key={r.id}>{r.title}</li>)}</ul>;
}

Unmounting: componentWillUnmount

componentWillUnmount runs just before the component is removed from the DOM. Use it to clean up anything that would cause a memory leak or unintended behaviour: timers, event listeners, open connections.

// Class component
class Timer extends React.Component {
  componentDidMount() {
    this.intervalId = setInterval(() => {
      this.setState((s) => ({ count: s.count + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.intervalId);
  }

  render() {
    return <p>{this.state.count}</p>;
  }
}

Hook equivalent: Return a cleanup function from useEffect. React calls it when the component unmounts (or before the effect re-runs if dependencies change).

// Functional component with hooks
function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount((c) => c + 1);
    }, 1000);

    return () => {
      clearInterval(intervalId); // cleanup runs on unmount
    };
  }, []);

  return <p>{count}</p>;
}

Preventing unnecessary re-renders: shouldComponentUpdate

shouldComponentUpdate lets you tell React whether a component needs to re-render when its props or state change. Returning false skips the render.

// Class component
class PureItem extends React.Component {
  shouldComponentUpdate(nextProps) {
    return nextProps.value !== this.props.value;
  }

  render() {
    return <li>{this.props.value}</li>;
  }
}

Hook equivalent: Wrap the component in React.memo. React will skip re-rendering the component if its props have not changed.

// Functional component with React.memo
const PureItem = React.memo(function PureItem({ value }) {
  return <li>{value}</li>;
});

Quick reference table

Class lifecycle methodHook equivalent
componentDidMountuseEffect(() => { ... }, [])
componentDidUpdate(prevProps, prevState)useEffect(() => { ... }, [deps])
componentWillUnmountReturn cleanup from useEffect
shouldComponentUpdateReact.memo()
constructor (initialising state)useState(initialValue)

Deprecated methods (do not use)

The following lifecycle methods were deprecated in React 16.3 and removed in React 18. If you see them in older code, they need to be replaced.

  • componentWillMount: ran before the first render. Replaced by the constructor or useEffect with [].
  • componentWillReceiveProps(nextProps): ran when new props arrived. Replaced by useEffect with the relevant prop in the dependency array.
  • componentWillUpdate(nextProps, nextState): ran before each update. Replaced by useEffect with dependencies.

These methods had the UNSAFE_ prefix added in React 16.3 (as UNSAFE_componentWillMount etc.) to signal their removal was coming. They are gone in React 18. If you are upgrading an older codebase, these will throw errors.

What to learn next

  • useEffect : the hook that replaces most lifecycle methods
  • Class Components : the full class component API and when you might still use one