Why We Use useEffect in React: Easy Explanation and Guide

Aniket DhaygudeAniket Dhaygude
4 min read

Introduction

React has revolutionized the way we build user interfaces, and with the introduction of Hooks in React 16.8, managing state and side effects has become simpler and more intuitive. One of the most powerful and commonly used Hooks is useEffect. In this guide, we will explore why we use useEffect, how it works, and how to use it effectively in your React applications.

Why Use useEffect?

1. Managing Side Effects

In React, side effects are operations that affect something outside the scope of the function being executed. Examples of side effects include:

  • Fetching data from an API

  • Subscribing to a data stream

  • Manually changing the DOM

  • Setting up timers or intervals

useEffect allows you to perform these side effects in functional components, keeping your code clean and maintaining the declarative nature of React.

2. Replacing Lifecycle Methods

Before Hooks, class components used lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount to manage side effects. useEffect consolidates these lifecycle methods into a single API, making the logic easier to manage and understand.

3. Dependency Management

useEffect provides a mechanism to specify dependencies, allowing you to control when the effect runs. This helps optimize performance by avoiding unnecessary re-renders and side effect executions.

How useEffect Works

Basic Syntax

The useEffect Hook takes two arguments:

  1. A function that contains the side effect.

  2. An optional array of dependencies.

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Your side effect code here
  }, []); // Dependency array

  return (
    <div>
      <h1>Hello, World!</h1>
    </div>
  );
}

Effect Without Dependencies

When no dependency array is provided, the effect runs after every render.

useEffect(() => {
  console.log('This runs after every render');
});

Effect With an Empty Dependency Array

When an empty dependency array is provided, the effect runs only once after the initial render (componentDidMount equivalent).

useEffect(() => {
  console.log('This runs only once after the initial render');
}, []);

Effect With Dependencies

When dependencies are provided, the effect runs only when one of the dependencies changes (componentDidUpdate equivalent).

useEffect(() => {
  console.log('This runs when count changes');
}, [count]);

Cleanup Function

useEffect can return a cleanup function that runs when the component unmounts or before the effect runs again (componentWillUnmount equivalent).

useEffect(() => {
  const timer = setTimeout(() => {
    console.log('This runs after 1 second');
  }, 1000);

  return () => {
    clearTimeout(timer);
    console.log('Cleanup');
  };
}, []);

Practical Examples

Fetching Data from an API

import React, { useState, useEffect } from 'react';

function DataFetchingComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch('https://api.example.com/data');
      const result = await response.json();
      setData(result);
    }

    fetchData();
  }, []); // Empty array ensures this runs only once

  return (
    <div>
      {data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading...</p>}
    </div>
  );
}

Setting Up a Timer

import React, { useState, useEffect } from 'react';

function TimerComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []); // Empty array ensures the interval is set up only once

  return (
    <div>
      <p>Count: {count}</p>
    </div>
  );
}

Common Pitfalls and Best Practices

1. Avoiding Infinite Loops

Ensure dependencies are correctly specified to avoid infinite loops. An effect without a dependency array or with changing dependencies on every render can lead to continuous re-renders.

2. Correctly Handling Cleanup

Always provide a cleanup function when side effects need to be cleaned up, such as clearing timers or unsubscribing from data streams.

3. Using Multiple useEffect Hooks

It's perfectly fine to use multiple useEffect hooks for different side effects, making your code more modular and easier to maintain.

useEffect(() => {
  // Effect 1
}, [dependency1]);

useEffect(() => {
  // Effect 2
}, [dependency2]);

Conclusion

useEffect is a powerful hook that simplifies the management of side effects in React functional components. By understanding how it works and following best practices, you can write clean, efficient, and maintainable React code. Whether you're fetching data, setting up timers, or handling subscriptions, useEffect provides a unified API to handle these operations effectively.

Now that you have a deep understanding of useEffect, you can confidently incorporate it into your React projects and harness the full power of React Hooks. Happy coding!

19
Subscribe to my newsletter

Read articles from Aniket Dhaygude directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aniket Dhaygude
Aniket Dhaygude

๐Ÿ‘จโ€๐Ÿ’ป Freelance Developer | UI/UX Enthusiast | Tech Blogger Transforming ideas into engaging digital experiences. I specialize in front-end development, UI/UX design, and creating dynamic web apps. Let's build something amazing together! ๐Ÿš€