useEffect

The useEffect hook is one of the most powerful and essential hooks in React. It allows you to perform side effects in function components, such as data fetching, subscriptions, or manually changing the DOM. Understanding how to use useEffect effectively can greatly enhance your ability to build dynamic and responsive React applications. This chapter will take you from the basics to advanced usage of useEffect, ensuring a comprehensive understanding.

Understanding useEffect

useEffect is a hook that runs side effects in your function components. Side effects include anything that affects something outside the scope of the function being executed, like fetching data from an API, setting up a subscription, or manually manipulating the DOM.

Basic Usage

The basic usage of useEffect involves two arguments: a function that contains the side effect code, and an optional array of dependencies that determine when the effect should re-run.

Example: Logging a Message

				
					import React, { useEffect } from 'react';

function MessageLogger() {
  useEffect(() => {
    console.log('Component mounted');
  }, []);

  return <div>Check the console</div>;
}

export default MessageLogger;

				
			

Explanation:

  • The effect runs after the first render and every time the dependencies change.
  • An empty dependency array [] means the effect runs only once after the initial render (componentDidMount equivalent).
				
					<div>Check the console</div>

				
			

In the console, you will see “Component mounted” logged once.

Dependencies and Cleanup

The dependencies array is crucial for controlling when the effect runs. If you omit the array, the effect runs after every render. You can also return a cleanup function from the effect to clean up resources.

Dependencies

Example: Updating the Document Title

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

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

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default DocumentTitle;

				
			

Explanation:

  • The effect updates the document title whenever count changes.
  • useEffect runs after every render where count has changed.
				
					Output :
<div>
  <h1>Count: 0</h1>
  <button>Increment</button>
</div>

				
			

When you click “Increment”, the document title updates to reflect the new count.

Cleanup

Example: Setting up a Timer

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

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((prevSeconds) => prevSeconds + 1);
    }, 1000);

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

  return <div>Seconds: {seconds}</div>;
}

export default Timer;

				
			

Explanation:

  • The effect sets up an interval to increment seconds every second.
  • The cleanup function clearInterval stops the interval when the component unmounts.
				
					Output :
<div>Seconds: 0</div>

				
			

The “Seconds” value increases by 1 every second.

Advanced Use Cases

Fetching Data

Example: Fetching Data from an API

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

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts/1')
      .then((response) => response.json())
      .then((json) => {
        setData(json);
        setLoading(false);
      });
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.body}</p>
    </div>
  );
}

export default DataFetcher;

				
			

Explanation:

  • The effect fetches data from an API when the component mounts.
  • setLoading updates the loading state, and setData stores the fetched data.
				
					Output:
<div>
  <h1>Post Title</h1>
  <p>Post body content...</p>
</div>

				
			

The component initially shows “Loading…” and then displays the fetched data.

Using Multiple useEffect Hooks

You can use multiple useEffect hooks in a single component to handle different side effects.

Example: Multiple Effects

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

function MultipleEffects() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');

  useEffect(() => {
    console.log('Count has changed:', count);
  }, [count]);

  useEffect(() => {
    console.log('Name has changed:', name);
  }, [name]);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <br />
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Enter your name"
      />
    </div>
  );
}

export default MultipleEffects;

				
			

Explanation:

  • One useEffect logs a message when count changes.
  • Another useEffect logs a message when name changes.
				
					Output:
<div>
  <h1>Count: 0</h1>
  <button>Increment</button>
  <br />
  <input type="text" value="" placeholder="Enter your name" />
</div>

				
			

Clicking “Increment” or typing in the input field triggers the respective effects.

Optimizing Performance

Dependency Management

Be mindful of dependencies to avoid unnecessary re-renders.

Example: Correct Dependency Management

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

function DependencyExample() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  useEffect(() => {
    console.log('Effect with count:', count);
  }, [count]);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <br />
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something"
      />
    </div>
  );
}

export default DependencyExample;

				
			

Explanation:

  • The effect runs only when count changes, not when text changes, optimizing performance.

Avoiding Unnecessary Effects

Ensure that effects only run when necessary.

Example: Conditional Effect

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

function ConditionalEffect() {
  const [isVisible, setIsVisible] = useState(true);

  useEffect(() => {
    if (isVisible) {
      console.log('Component is visible');
    }
  }, [isVisible]);

  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>
        Toggle Visibility
      </button>
    </div>
  );
}

export default ConditionalEffect;

				
			

Explanation:

  • The effect runs only when isVisible changes, preventing unnecessary console logs.

Example : Fetching Data from an API

In this example, we’ll create a simple React app that fetches data from an API and displays it. We’ll use useEffect to fetch the data when the component mounts.

App Component

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

function App() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts/1')
      .then((response) => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then((json) => {
        setData(json);
        setLoading(false);
      })
      .catch((error) => {
        setError(error);
        setLoading(false);
      });
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.body}</p>
    </div>
  );
}

export default App;

				
			

Explanation:

State Initialization:

  • data is initialized as null.
  • loading is initialized as true.
  • error is initialized as null.

Effect Hook:

  • The effect runs once when the component mounts because the dependencies array is empty ([]).
  • The fetch function is used to get data from the API.
  • If the response is successful, the data is stored in data state, and loading is set to false.
  • If there is an error, it is caught and stored in error state, and loading is set to false.

Conditional Rendering:

  • If loading is true, it displays “Loading…”.
  • If error is not null, it displays the error message.
  • Otherwise, it displays the fetched data.

Output:

When you run this component, you’ll see “Loading…” initially. Once the data is fetched, it will display the title and body of the post fetched from the API. If there’s an error, it will display the error message.

Example : Dark Mode Toggle

In this example, we’ll create a React app that allows users to toggle between light mode and dark mode. We’ll use useEffect to update the document’s body class based on the current mode.

App Component

				
					import React, { useState, useEffect } from 'react';
import './App.css'; // Assume this file contains styles for light and dark modes

function App() {
  const [isDarkMode, setIsDarkMode] = useState(false);

  useEffect(() => {
    if (isDarkMode) {
      document.body.classList.add('dark-mode');
    } else {
      document.body.classList.remove('dark-mode');
    }

    // Cleanup function to remove class when component unmounts
    return () => {
      document.body.classList.remove('dark-mode');
    };
  }, [isDarkMode]);

  return (
    <div className="App">
      <h1>{isDarkMode ? 'Dark Mode' : 'Light Mode'}</h1>
      <button onClick={() => setIsDarkMode(!isDarkMode)}>
        Toggle Mode
      </button>
    </div>
  );
}

export default App;

				
			

App.css

				
					body {
  background-color: white;
  color: black;
}

.dark-mode {
  background-color: black;
  color: white;
}

.App {
  text-align: center;
  margin-top: 50px;
}

button {
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
}

				
			

Explanation:

State Initialization:

  • isDarkMode is initialized as false.

Effect Hook:

  • The effect runs whenever isDarkMode changes.
  • If isDarkMode is true, the dark-mode class is added to the document’s body.
  • If isDarkMode is false, the dark-mode class is removed.
  • The cleanup function ensures that the dark-mode class is removed if the component unmounts.

Button Click:

  • Clicking the button toggles the isDarkMode state between true and false.

Output:

When you run this component, you’ll see a button labeled “Toggle Mode” and a heading displaying either “Light Mode” or “Dark Mode” depending on the current state. Clicking the button toggles the mode and updates the document’s body class, changing the background color and text color accordingly.

The useEffect hook is a cornerstone of React's functionality for managing side effects in functional components. By understanding its basics, dependencies, cleanup functions, and advanced use cases, you can effectively manage side effects in your React applications. This chapter provided an in-depth exploration of useEffect, from simple logging to complex data fetching and performance optimization. Mastering useEffect will significantly enhance your ability to create dynamic, responsive, and efficient React applications.Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India