React useEffect Hook Explained with Examples and Best Practices bilal shafqat

React useEffect Hook Explained with Examples and Best Practices

Introduction: Why You Need useEffect() in React

If you’ve ever wondered how to fetch data, listen to scroll events, or update the DOM in a functional component — the answer is useEffect().

In React, components are declarative and reactive by design. But to handle side effects — like API calls, timers, or subscriptions — you need a special hook: useEffect().

It’s one of the most powerful but also most misunderstood hooks in the React ecosystem. This guide explains what useEffect() does, how it works, and how to use it correctly in 2025 and beyond.

What is useEffect()?

What is useEffect bilal shafqat

useEffect() is a built-in React hook that lets you run side effects in your functional components.

What’s a side effect?

A side effect is anything that:

  • Interacts with the outside world (APIs, localStorage)
  • Affects something outside the component scope (event listeners, timeouts)
  • Doesn’t happen as a result of just rendering JSX

Basic Syntax:

useEffect(() => {
  // Side effect logic
}, [dependencies]);
  • The first argument is a function that runs your effect
  • The second argument is a dependency array that tells React when to run the effect

Why is useEffect Important?

Why is useEffect Important bilal shafqat

Before React 16.8, stateful logic and side effects were handled in class components using lifecycle methods like componentDidMount() and componentDidUpdate().

With hooks, useEffect() replaces these methods in function components — offering a more flexible and cleaner way to run side effects.

You use it for:

  • Fetching data
  • Setting up timers or intervals
  • Subscribing/unsubscribing to events
  • Performing animations
  • Updating document title or localStorage

Example 1: Fetching Data on Component Mount

import { useEffect, useState } from 'react';

function Users() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    async function fetchData() {
      const res = await fetch('https://api.example.com/users');
      const data = await res.json();
      setUsers(data);
    }
    fetchData();
  }, []); // run once on mount

  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

Dependency Array: The Most Important Part

The second argument to useEffect() is the dependency array, which determines when the effect runs.

Dependency Array When the Effect Runs
[] On mount (once only)
[count] On mount and when count changes
None On every render (not recommended)

Example with dependencies:

useEffect(() => {
  console.log("Count changed!");
}, [count]);

Avoiding Infinite Loops

A common mistake in useEffect is causing infinite re-renders by:

  • Forgetting the dependency array
  • Updating state inside the effect that also triggers the same effect

Bad:

useEffect(() => {
  setCount(count + 1);
});

Fix:

useEffect(() => {
  // safe logic here
}, [count]);

Or use functional updates:

setCount(prev => prev + 1);

Cleanup Functions in useEffect

Some side effects — like event listeners or intervals — need cleanup to prevent memory leaks or unwanted behaviors.

Example: Timer cleanup

useEffect(() => {
  const id = setInterval(() => {
    console.log('Tick');
  }, 1000);

  return () => clearInterval(id);
}, []);

Avoiding Common useEffect Mistakes

1. Forgetting to list all dependencies

useEffect(() => {
  doSomething(message);
}, [message]);

2. Using async directly in useEffect

useEffect() cannot be marked async because it expects a synchronous return value.

Fix:

useEffect(() => {
  async function fetchData() {
    const res = await fetch('/data');
    const result = await res.json();
    setData(result);
  }

  fetchData();
}, []);

3. Using useEffect unnecessarily

If you can compute something directly in JSX or with useMemo(), do that instead.

Best Practices for useEffect (2025)

  • Keep each useEffect focused on one task
  • Use multiple useEffects instead of combining logic
  • Always include all dependencies
  • Use named async functions inside effects
  • Clean up subscriptions and timers

When Not to Use useEffect

You don’t always need useEffect. You might be overusing it if:

  • You’re setting state derived from props (compute it directly)
  • You’re using it to log state (use dev tools instead)
  • You’re using it instead of useMemo() or useCallback()

Ask yourself:

“Am I doing something after render that affects the outside world?”
If yes — use it. If not — you might not need it.

Summary

useEffect() is the most versatile React hook for handling side effects in function components.

Key takeaways:

  • It replaces lifecycle methods from class components
  • The dependency array controls when it runs
  • Cleanup functions prevent memory leaks
  • Common mistakes include missing dependencies and misusing async

Use it wisely, and your React apps will be cleaner, more predictable, and easier to maintain.

Leave A Comment