Component Lifecycle

By Syncfusion®, Inc. · Last updated

Mounting, updating and unmounting, how rendering and effects fit together, managing the lifecycle with useEffect, effect dependencies and dependency arrays, cleanup functions, and avoiding infinite effect loops.

Introduction to Component Lifecycle

The lifecycle of a component is the set of stages it goes through: it appears on the screen, it changes, and it is removed from the screen.

Knowing the lifecycle tells you when your code runs. For example: load data when the component appears, and stop a timer when it is removed.

Real-World Example

A bookshop's OrderTracker component appears when the user opens an order. In useEffect, it starts a timer that asks the server for the delivery status every 10 seconds.

When the user leaves the page, React removes the component and runs the cleanup function, which stops the timer. Without the cleanup, the requests would continue in the background.

Every component has three stages: mounting (appears), updating (changes), and unmounting (is removed).

Lifecycle Phases and React Rendering

The three stages are called phases.

Lifecycle: Mounting, then Updating (can repeat many times), then Unmounting

Mounting appears on screen

Updating state or props change

Unmounting removed from screen can happen many times

A component mounts once, may update many times, and unmounts once.

Each time a component goes through a phase, React renders it: it runs the component function to get the new TSX. Then React updates the screen. After the screen is updated, React runs your effects.

Every phase: React renders, updates the screen, then runs effects

1. Render run the component

2. Update screen change the page

3. Run effects useEffect code

Effects always run last, after the screen is updated.

  • Render — React runs your component function. Do not do side work here, only work out the TSX.
  • Update the screen — React changes the page to match.
  • Run effects — React runs your useEffect code. This is where side work goes, such as timers and loading data.

Mounting Phase

Mounting is when a component appears on the screen for the first time. It happens once.

In the mounting phase:

  1. React runs the component function. State gets its starting values.
  2. React puts the TSX on the screen.
  3. React runs every useEffect in the component.

To run code only once, when the component mounts, use an effect with an empty dependency array [].

useEffect(() => {
  console.log("Shop opened");
}, []); // [] means: only after mounting

Updating Phase

Updating happens when the component's state or props change. React renders the component again and updates the screen. This can happen many times.

To run code after an update, put the value you care about in the dependency array. The effect then runs after mounting, and again each time that value changes.

useEffect(() => {
  console.log("Cart now has " + count + " books");
}, [count]); // runs when count changes

Unmounting Phase

Unmounting is when a component is removed from the screen. For example, a pop-up is closed, or the user goes to another page.

Before it is removed, a component should clean up anything it started: stop timers, stop listening to events, and cancel work that is still running. You do this with a cleanup function, which you return from the effect.

useEffect(() => {
  console.log("Shop opened");
  return () => {
    console.log("Shop closed"); // runs when the component is removed
  };
}, []);

Lifecycle Management with useEffect

In function components, useEffect handles all three phases. You choose the phase with the dependency array and the cleanup function.

You want to run code...Write
Once, when the component mountsuseEffect(() => { ... }, [])
When a value changes (and on mount)useEffect(() => { ... }, [value])
After every renderuseEffect(() => { ... }) (no array)
When the component unmountsreturn () => { ... } inside an effect with []

Component Lifecycle Flow with Examples

The lab below logs every phase. BookPanel has three effects: one for mount and unmount, one for updates, and one that runs after every render.

Read the console as you try each step:

  1. On load: "Render", then "Mounted", then "Effect: likes is now 0".
  2. Click Like: "Render: likes = 1", then "Effect: likes is now 1". The mount effect does not run again.
  3. Click Hide panel: "Unmounted: BookPanel removed". The cleanup function ran.
  4. Click Show panel: the panel mounts again, from the start, with likes = 0.

"Render" always comes before the effects. React first works out the TSX and updates the screen. Only then does it run your effects.

Common Lifecycle Scenarios

Most effects you write will follow one of these patterns.

Scenario 1: Load Data When the Component Mounts

useEffect(() => {
  fetchBooks().then((list) => setBooks(list));
}, []);

Scenario 2: Run Again When a Value Changes

When the user picks a new category, load the books for that category.

useEffect(() => {
  fetchBooks(category).then((list) => setBooks(list));
}, [category]);

Scenario 3: Start Something, and Stop It on Unmount

Start a timer, an event listener or a connection, and stop it in the cleanup.

useEffect(() => {
  const id = setInterval(() => setSeconds((s) => s + 1), 1000);
  return () => clearInterval(id);
}, []);

Scenario 4: Keep Something Outside React in Step with State

useEffect(() => {
  document.title = count + " books in cart";
}, [count]);

Effect Dependencies

A dependency is a value from your component that the effect uses, such as a state, a prop, or a variable made from them.

Every value the effect uses should be in the dependency array. Then React knows to run the effect again when one of them changes.

useEffect(() => {
  console.log("Showing " + category + " books, page " + page);
}, [category, page]); // both values are used, so both are listed

If you leave one out, the effect uses an old value of it. This is a common bug.

Click Biography. The "Right" line changes. The "Wrong" line still says "Fiction", because its effect ran only once, on mount.

Dependency Arrays

The dependency array is the second input to useEffect. It decides when the effect runs. There are three forms.

Which renders run the effect: no array runs every time, empty array only the first, [likes] the first and when likes changes mount likes views likes what changed in each render useEffect(fn)

useEffect(fn, [])

useEffect(fn, [likes])

Green means the effect runs in that render. The array decides when.

FormWhen the effect runsUse it for
No array: useEffect(fn)After every renderRarely needed
Empty: useEffect(fn, [])Once, after mountingLoading data once, starting a timer
With values: useEffect(fn, [a, b])After mounting, and when a or b changesReacting to a change

On load, all three effects run. Click View: only "No array" runs. Click Like: "No array" and "[likes]" run. The "[]" effect never runs again.

Cleanup Functions

A cleanup function is the function you return from an effect. React runs it at two times:

  • Before the effect runs again, because a dependency changed. This cleans up the old run.
  • When the component unmounts.

Without cleanup, timers keep running and listeners keep listening after the component is gone. This wastes memory and can cause errors.

Cleanup runs before the effect runs again, and when the component is removed

Effect start timer

Cleanup stop timer

Effect start again

Cleanup on unmount a dependency changed component removed

Every effect that starts something should stop it in its cleanup.

The seconds go up. Click Stop reading. The console says "Timer stopped", because the cleanup ran clearInterval(id). Click Start reading, and a new timer starts from 0.

Look at these lines:

const id = setInterval(...);    // start: keep the timer's id
return () => clearInterval(id); // cleanup: stop that timer

The effect keeps the timer's id, so the cleanup knows which timer to stop.

Preventing Infinite Effect Loops

An infinite loop happens when an effect changes a value that is also in its own dependency array. The effect runs, changes the value, so it runs again, changes it again, and so on forever. The page freezes or React stops it with an error.

Infinite loop: the effect sets count, count changes, the effect runs again, forever

Effect runs setCount(count + 1)

count changes React re-renders

[count] changed run the effect again

This circle never stops. Add a stop condition, or do not use an effect.

// Wrong: count changes, so the effect runs, so count changes...
useEffect(() => {
  setCount(count + 1);
}, [count]);

Another common cause is an effect with no array that sets state. Every render runs the effect, the effect sets state, and setting state causes a render.

// Wrong: no array, and the effect sets state
useEffect(() => {
  setBooks(loadBooks());
});

How to avoid loops:

  • Always write a dependency array. Use [] for "once".
  • Do not set a state that the effect depends on, unless there is a condition that stops it.
  • Ask: do I need an effect at all? If you can work out a value from props or state, just calculate it in the component. No effect and no extra state needed.
// No effect needed: just calculate it
const total = books.length * 299;

This effect depends on stock and changes stock. It does not loop forever, because of if (stock > 0). When the stock reaches 0, the effect stops changing it.

Before you write an effect, ask: "Is this work outside React?" If not, you probably do not need an effect.

Common mistakes

MistakeWhat you seeFix
Effect with no dependency array that sets stateThe page freezes: an infinite loopAdd a dependency array
Setting a state that is in the effect's own arrayAn infinite loopAdd a stop condition, or remove the effect
Leaving out a value the effect usesThe effect uses an old valueList every value the effect uses
Forgetting to clear a timer or listenerIt keeps running after the component is goneReturn a cleanup function
Doing side work (like timers) directly in the component bodyIt runs on every renderMove it into useEffect
Using an effect to calculate a value from stateExtra renders and extra stateCalculate it directly in the component
Expecting [] effects to see new stateThe effect shows the starting valueAdd the state to the array

Check Yourself

Exercises

What to Remember

  • A component mounts once, updates many times, and unmounts once. Effects run after the screen updates.
  • The dependency array decides when an effect runs: none = every render, [] = once, [a] = when a changes.
  • Return a cleanup function to stop timers and listeners, and add a stop condition so an effect never loops forever.

Next: Application State Management