React UseEffect With Async Functions , Using useEffect
with asynchronous functions in React is essential for managing side effects that occur during the component’s lifecycle, particularly for operations that are asynchronous, such as data fetching, subscriptions, or timers. Here are several reasons why and how we use useEffect
with async operations:
Knowing Why We Use
useEffect
for Async Operations first you have to know What is React UseEffect , and What is Async function
What is React UseEffect :
Before we going to Read about React UseEffect With Async Functions we have to understand about useEffect
.
useEffect
is a hook in React that lets you perform side effects in functional components. It is a fundamental tool for managing component lifecycle events, such as fetching data, subscribing to a service, and manually changing the DOM, among other tasks. Essentially, useEffect
allows you to run code at specific points in the component’s lifecycle: after it renders, after each update, or just before it unmounts.
Syntax
The basic syntax of useEffect
is:
useEffect(() => {
// Your effect code here
return () => {
// Cleanup code here (optional)
};
}, [dependencies]);
How useEffect
Works
- Effect Function: The first argument to
useEffect
is the effect function, which contains the code you want to run as the side effect. - Cleanup Function: The effect function can return a cleanup function, which React will call before the component unmounts or before running the effect again when dependencies change. This is useful for cleaning up resources such as subscriptions, timers, or event listeners.
- Dependency Array: The second argument is an optional array of dependencies. The effect runs whenever any of the dependencies change. If you pass an empty array
[]
, the effect will run only once when the component mounts and clean up when the component unmounts.
Basic Example
import React, { useState, useEffect } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
// Cleanup function (optional)
return () => {
console.log('Cleanup code if necessary');
};
}, [count]); // Dependency array: this effect runs when 'count' changes
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
};
export default ExampleComponent;
Key Points
- Component Did Mount and Update: Without a dependency array,
useEffect
runs after every render. - Component Did Mount: With an empty dependency array
[]
,useEffect
runs only once after the initial render. - Component Did Update: With a specific dependency array,
useEffect
runs only when those dependencies change. - Cleanup: The function returned from
useEffect
is run before the component unmounts and before running the effect next time.
What is Async function
Understanding Async and Sync Functions
Synchronous (Sync) Functions: In synchronous programming, operations are executed one after another. Each operation must complete before the next one starts. This can lead to blocking, where an operation that takes a long time (like reading a file or making a network request) prevents subsequent operations from running until it finishes.
Asynchronous (Async) Functions: In asynchronous programming, operations can start and run independently of other operations. The program can continue executing subsequent operations without waiting for the previous ones to complete. This is particularly useful for I/O-bound tasks like network requests, file reading/writing, or database operations.
Real-Life Analogy
Synchronous Example: Imagine you’re cooking dinner, and you need to boil pasta and prepare the sauce. In a synchronous approach, you would:
- Fill a pot with water and wait until it boils (5 minutes).
- Once the water is boiling, add the pasta and wait until it’s cooked (10 minutes).
- Once the pasta is done, start preparing the sauce (5 minutes).
Total time: 20 minutes.
Asynchronous Example: In an asynchronous approach, you would:
- Fill a pot with water and put it on the stove to boil (5 minutes). While waiting, you start preparing the sauce.
- When the water boils, you add the pasta and continue working on the sauce.
- Both tasks are done almost simultaneously.
Total time: around 10 minutes (overlapping tasks).
Real-Life Code Examples
Synchronous Function:
function syncOperation() {
const result = longRunningTask(); // This task takes a long time to complete
console.log(result);
}
function longRunningTask() {
// Simulate a task that takes time, like reading a large file
let sum = 0;
for (let i = 0; i < 1e9; i++) {
sum += i;
}
return sum;
}
syncOperation();
console.log("This will log after the long-running task completes.");
Asynchronous Function:
codeasync function asyncOperation() {
try {
const result = await longRunningTaskAsync(); // Await a promise
console.log(result);
} catch (error) {
console.error(error);
}
}
function longRunningTaskAsync() {
return new Promise((resolve) => {
// Simulate a task that takes time using setTimeout
setTimeout(() => {
resolve("Task Complete");
}, 3000); // 3 seconds delay
});
}
asyncOperation();
console.log("This will log immediately, before the async task completes.");
Where to Use Async vs. Sync Functions
Use Asynchronous Functions When:
- I/O-bound Operations: Tasks that involve waiting for external resources, like network requests, file reading/writing, or database queries.
- Concurrency: When you need to perform multiple operations concurrently without blocking the main thread, improving performance and responsiveness.
- User Interfaces: In front-end development, to keep the UI responsive while waiting for data or performing background tasks.
Avoid Asynchronous Functions When:
- CPU-bound Tasks: Tasks that require heavy computation and do not benefit from being asynchronous. JavaScript, being single-threaded, doesn’t gain much from async for CPU-heavy operations without worker threads.
- Sequential Dependencies: When tasks must be executed in a strict order and cannot proceed until the previous one completes. Although async can still be used here, it may complicate the code unnecessarily.
Summary
- Synchronous Functions: Block execution until complete, suitable for simple, sequential tasks.
- Asynchronous Functions: Do not block execution, allowing other operations to run concurrently, suitable for tasks involving waiting for I/O operations.
Using asynchronous programming effectively can lead to more efficient and responsive applications, particularly in environments where I/O-bound tasks are common, such as web development.
Why We Use useEffect
for Async Operations
- Component Lifecycle Management:
useEffect
allows you to run code at specific points in the component’s lifecycle (e.g., after the component mounts, when it updates, or before it unmounts). This is crucial for initializing data, setting up subscriptions, and performing cleanups. - Data Fetching: Most applications need to fetch data from an external source. Using
useEffect
, you can trigger a fetch request when the component mounts and update the component’s state with the fetched data. - Side Effects: Any operation that affects something outside the scope of the function, such as network requests or manual DOM manipulations, is considered a side effect.
useEffect
is designed to handle such operations. - Cleanup:
useEffect
supports returning a cleanup function, which can be used to cancel subscriptions, clear timers, or abort fetch requests when the component unmounts or before the effect re-runs.
How to Use useEffect
with Async Functions
- Define the Async Function Inside
useEffect
: SinceuseEffect
itself cannot be asynchronous, define an async function inside it and call it immediately. - Manage State and Errors: Use
useState
to manage loading, data, and error states to provide feedback to the user and handle various states of the async operation. - Handle Dependencies: Use the dependency array to control when the effect should run (e.g., once on mount, or when certain variables change).
Here is an example:
import React, { useEffect, useState } from 'react';
const MyComp = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []); // Empty dependency array means this effect runs once when the component mounts
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div>
<h1>Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default MyComp;

Explanation:
- State management: The component uses
useState
to managedata
,loading
, anderror
states. - Async function inside
useEffect
:fetchData
is defined as an async function inside theuseEffect
. It handles the data fetching and updates the state accordingly. - Error handling: The async function includes a try-catch block to handle errors that might occur during the fetch operation.
- Loading state: The
loading
state is used to conditionally render a loading message while the data is being fetched. - Effect dependencies: The empty dependency array
[]
ensures that the effect runs only once when the component mounts.
If You Want to know About React Query Click here
FAQ :
Can we use an async function in useEffect?
Yes, you can use an async function inside useEffect
in React, but you need to define the async function inside the useEffect
and then call it. This is because the useEffect
function itself cannot be made async directly.
Is useEffect synchronous or asynchronous?
useEffect
itself is neither synchronous nor asynchronous. However, the functions you call within useEffect
can be either synchronous or asynchronous. The behavior of useEffect
depends on the code you place inside it and how you handle side effects.
Why is useEffect called twice?
In development mode, React’s useEffect
(along with other lifecycle methods) may be called twice due to React’s Strict Mode. This behavior is intentional and designed to help developers identify side effects that might be causing bugs or inconsistent behavior in their applications.
[…] about why we use Cleanup function in react application , What is the use of cleanup function in useEffect , If you don’t know the use of useEffect click here What is UseEffect […]