How to Fetch API Data in React Using Axios (With Bonus Advanced Tips)
Master data fetching in React with Axios—learn how to set it up, handle loading states, errors, organize your API logic, and scale it for production-ready applications.
Fetching data from APIs is a fundamental part of React applications. Axios makes it easier with automatic JSON parsing, robust error handling, interceptors, and support for request cancellation and progress tracking. Let’s explore how to implement it step-by-step—and then optimize it for clean architecture and better developer experience.
🛠 Setup & Basic GET Request
- Install Axios:
npm install axios
- Use
useEffect
&useState
to fetch data on mount:
import React, { useState, useEffect } from "react";
import axios from "axios";
function App() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/posts")
.then(res => {
setPosts(res.data);
})
.catch(err => {
setError(err.message);
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) return Loading...
;
if (error) return Error: {error}
;
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default App;
✅ Why Axios?
- Automatic JSON parsing – No need to manually call
res.json()
. - Error objects are detailed – You get status codes, headers, and data.
- Supports cancellation, timeouts, and interceptors out of the box.
🔄 Using Async/Await for Cleaner Code
useEffect(() => {
const fetchData = async () => {
try {
const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
setPosts(res.data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
📚 Organize with Axios Instances & Services
To keep things clean and scalable, create an Axios client:
// apiClient.js
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
headers: { 'Content-Type': 'application/json' },
timeout: 5000,
});
apiClient.interceptors.response.use(
res => res,
error => {
console.error('API Error:', error);
return Promise.reject(error);
}
);
export default apiClient;
Create service modules for endpoints:
// postService.js
import apiClient from './apiClient';
export const getPosts = () => apiClient.get('/posts');
export const createPost = (data) => apiClient.post('/posts', data);
📤 Submitting Data via POST
const handleSubmit = async () => {
try {
await createPost({ title: 'New Post', body: 'Post content' });
alert('Post created!');
} catch (err) {
console.error('Submit failed:', err.message);
}
};
📎 Cancel Requests in useEffect
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
const res = await axios.get('/posts', { signal: controller.signal });
setPosts(res.data);
} catch (err) {
if (axios.isCancel(err)) {
console.log('Request canceled');
} else {
setError(err.message);
}
} finally {
setLoading(false);
}
};
fetchData();
return () => controller.abort();
}, []);
📦 Bonus: Caching with React Query
For features like background refetching and stale data caching:
import { useQuery } from '@tanstack/react-query';
import apiClient from './apiClient';
function usePosts() {
return useQuery(['posts'], () =>
apiClient.get('/posts').then(res => res.data)
);
}
function PostList() {
const { data, isLoading, error } = usePosts();
if (isLoading) return <p>Loading posts...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
);
}
📊 Handling Pagination
To paginate responses:
apiClient.get('/posts', { params: { _page: 1, _limit: 10 } });
🧯 Add Error Boundaries
For production apps, wrap components with error boundaries to catch unexpected crashes:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return <p>Something went wrong.</p>;
return this.props.children;
}
}
🔐 Security Best Practices
- Never store sensitive tokens directly in code—use environment variables.
- Always set
withCredentials
for authenticated requests if required. - Use HTTPS in production to prevent data interception.
📌 Takeaways
- Axios simplifies data fetching in React with powerful features out of the box.
- Use async/await for cleaner syntax and error handling.
- Centralize API calls via Axios instances and service files.
- Use cancellation tokens to prevent memory leaks on unmount.
- React Query offers effortless caching and background updates.
- Add pagination and error boundaries to scale your UI safely.
Web Expert Solution delivers practical, production-grade React and JavaScript content. Follow us for more deep dives into scalable API architecture, frontend optimization, and clean coding practices for modern developers.