How to use async/await in React data fetching
Using async/await in React data fetching provides cleaner asynchronous code compared to promises, making API calls more readable and easier to debug in functional components. As the creator of CoreUI, a widely used open-source UI library, I’ve implemented async/await patterns in thousands of React components for data fetching in enterprise applications. From my expertise, the most effective approach is creating async functions inside useEffect with proper error handling and loading states. This method provides clean asynchronous operations with excellent error management and user experience.
Create async functions inside useEffect for data fetching with proper error handling and loading states.
import { useState, useEffect } from 'react'
function UserList() {
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true)
const response = await fetch('/api/users')
if (!response.ok) throw new Error('Failed to fetch')
const data = await response.json()
setUsers(data)
} catch (err) {
setError(err.message)
} finally {
setLoading(false)
}
}
fetchUsers()
}, [])
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error}</div>
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
The async function is defined inside useEffect to handle the asynchronous fetch operation. Use try/catch blocks for error handling and finally blocks for cleanup operations like setting loading states. The useEffect dependency array controls when the fetch operation runs.
Best Practice Note:
This is the same async/await pattern we use in CoreUI React components for reliable data fetching with proper loading states and error handling.