State management
State change events
Programming tips
React development
Event handling

How can I run an action when a state changes?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In React, the standard way to run an action after state changes is useEffect. The important detail is that you should use it for real side effects such as network calls, logging, subscriptions, or imperative APIs, not for simple derived values that could be computed during render.

Use useEffect for Side Effects

If you want code to run whenever a specific piece of state changes, put that state in the dependency array.

jsx
1import { useEffect, useState } from 'react';
2
3export default function Counter() {
4  const [count, setCount] = useState(0);
5
6  useEffect(() => {
7    console.log('count changed:', count);
8  }, [count]);
9
10  return (
11    <button onClick={() => setCount(count + 1)}>
12      Count: {count}
13    </button>
14  );
15}

React runs the effect after the render that reflects the new state value.

Do Not Use an Effect for Derived State

A common mistake is using useEffect just to mirror one state value into another when the result could be calculated directly.

Bad pattern:

jsx
1const [firstName, setFirstName] = useState('Ada');
2const [lastName, setLastName] = useState('Lovelace');
3const [fullName, setFullName] = useState('');
4
5useEffect(() => {
6  setFullName(`${firstName} ${lastName}`);
7}, [firstName, lastName]);

Better pattern:

jsx
const fullName = `${firstName} ${lastName}`;

Use an effect only when something external needs to happen because state changed.

Fetch Data When a State Value Changes

A common real use case is refetching when a filter or selected ID changes.

jsx
1import { useEffect, useState } from 'react';
2
3export default function UserProfile({ userId }) {
4  const [user, setUser] = useState(null);
5
6  useEffect(() => {
7    let cancelled = false;
8
9    async function load() {
10      const response = await fetch(`/api/users/${userId}`);
11      const data = await response.json();
12      if (!cancelled) {
13        setUser(data);
14      }
15    }
16
17    load();
18    return () => {
19      cancelled = true;
20    };
21  }, [userId]);
22
23  return <pre>{JSON.stringify(user, null, 2)}</pre>;
24}

This is exactly the kind of state-driven action useEffect is meant for.

Reacts to Props Too

In React, props changes also trigger rerenders, so the same pattern works when the "state change" you care about actually comes from parent props, context, or other reactive inputs.

The dependency list should match the value that the effect logically depends on, whether that value came from useState, props, or another hook.

Cleanup Matters

If the action creates a subscription, timer, or other persistent resource, return a cleanup function.

jsx
1useEffect(() => {
2  const id = setInterval(() => {
3    console.log('tick');
4  }, 1000);
5
6  return () => clearInterval(id);
7}, []);

Without cleanup, repeated state changes can leak timers, listeners, or stale async work.

Not Every Action Needs an Effect

If the action belongs exactly to the event that changed the state, it can be cleaner to perform it in the event handler itself instead of waiting for an effect. Effects are best when the logic depends on the rendered state value or on coordination with external systems, not when the cause and response already live in the same click or input handler.

Infinite Loops Are the Main Warning Sign

If an effect updates the same state that triggers it, you can create a render-effect-update loop accidentally. When that happens, the real fix is usually to rethink whether the effect should exist at all or whether the update can be moved to the event or data-loading boundary instead.

Common Pitfalls

  • Using useEffect for values that can be derived directly during render.
  • Forgetting a dependency and then wondering why the effect does not run on the right state change.
  • Adding unnecessary dependencies and causing the effect to run more often than intended.
  • Triggering state updates inside an effect in a way that causes accidental loops.
  • Forgetting cleanup for subscriptions, timers, or in-flight async work.

Summary

  • In React, useEffect is the normal tool for running actions after state changes.
  • Use it for side effects, not for simple derived values.
  • Put the relevant state or prop in the dependency array.
  • Return a cleanup function when the effect allocates persistent resources.
  • Most effect bugs come from wrong dependencies or using effects for the wrong job.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.