React
API Calls with Fetch
Build dynamic React components that pull live data from APIs using JavaScript's built-in fetch function.
API calls transform static React components into dynamic applications. Every modern web app — Netflix loading movie data, Airbnb fetching property listings, or Linear syncing project updates — relies on API communication. Your DataFlow dashboard currently displays hardcoded analytics data. Real dashboards pull fresh numbers from servers. Today you'll connect React components to external data sources using fetch, JavaScript's native HTTP client.Why Fetch Over Other Methods
JavaScript offers multiple ways to make HTTP requests. XMLHttpRequest came first but requires verbose syntax. jQuery's$.ajax() simplified things but adds library weight.
Fetch is built into modern browsers. No external dependencies. Promise-based for clean async code. Here's how they compare:
Fetch Benefits
Native browser API, Promise-based, lightweight, modern syntax
XMLHttpRequest
Callback-based, verbose setup, harder error handling
Basic Fetch Syntax
Fetch takes a URL and returns a Promise. The response needs conversion to usable JavaScript data:// Simple fetch request
fetch('https://api.example.com/revenue')
.then(response => response.json())
.then(data => {
console.log(data); // Use the data
});function RevenueCard() {
const [revenue, setRevenue] = useState(0);
useEffect(() => {
fetch('/api/revenue')
.then(response => response.json())
.then(data => setRevenue(data.total));
}, []);
return <h2>${revenue.toLocaleString()}</h2>;
}What just happened?
The component starts with revenue: 0 and loading: true. useEffect runs after first render, simulates an API call, then updates state. React re-renders with fresh data. Try modifying the timeout duration to see loading states.
Handling Loading States
Users hate blank screens while APIs load. Professional apps show loading indicators. Here's the pattern:function UsersList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then(response => response.json())
.then(data => {
setUsers(data);
setLoading(false);
});
}, []);
if (loading) return <div>Loading users...</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}loading is true, show a spinner. Once data arrives, render the list. This prevents flickering empty states.
Error Handling
Networks fail. APIs go down. Servers return errors. Your React components must handle these gracefully:function StatsCard() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/api/stats')
.then(response => {
if (!response.ok) {
throw new Error('Failed to load stats');
}
return response.json();
})
.then(data => setData(data))
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>Orders: {data.orders}</div>;
}Critical Detail
Fetch doesn't reject on HTTP error status codes like 404 or 500. You must check response.ok manually. This trips up many developers coming from other HTTP libraries.
Async/Await Syntax
Promise chains with.then() get messy. Modern JavaScript prefers async/await for cleaner code:
function OrdersList() {
const [orders, setOrders] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchOrders = async () => {
try {
const response = await fetch('/api/orders');
if (!response.ok) throw new Error('Failed');
const data = await response.json();
setOrders(data);
} catch (error) {
console.error('Fetch error:', error);
} finally {
setLoading(false);
}
};
fetchOrders();
}, []);
// Render logic here
}POST Requests and Form Data
Reading data is half the story. Most apps need to send data too. The DataFlow team wants users to create custom analytics reports:function CreateReport() {
const [title, setTitle] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
try {
const response = await fetch('/api/reports', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ title, type: 'revenue' })
});
if (response.ok) {
alert('Report created!');
setTitle('');
}
} catch (error) {
alert('Failed to create report');
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Report title"
/>
<button disabled={loading}>
{loading ? 'Creating...' : 'Create Report'}
</button>
</form>
);
}Complete DataFlow Example
Here's how the DataFlow StatsBar component loads all four KPI metrics from an API:function StatsBar() {
const [stats, setStats] = useState({
revenue: 0, users: 0, orders: 0, growth: 0
});
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadStats = async () => {
try {
const response = await fetch('/api/dashboard/stats');
const data = await response.json();
setStats(data);
} catch (error) {
console.error('Stats failed:', error);
} finally {
setLoading(false);
}
};
loadStats();
}, []);
const StatCard = ({ label, value, prefix = '' }) => (
<div style={{ padding: '16px', background: '#f8fafc' }}>
<h4>{label}</h4>
<div>{loading ? '...' : prefix + value.toLocaleString()}</div>
</div>
);
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '16px' }}>
<StatCard label="Revenue" value={stats.revenue} prefix="$" />
<StatCard label="Users" value={stats.users} />
<StatCard label="Orders" value={stats.orders} />
<StatCard label="Growth" value={stats.growth} prefix="+" />
</div>
);
}What just happened?
The component loads with placeholder stats, makes an API call, then updates all four cards simultaneously. The StatCard subcomponent handles the loading state with "..." text. Try refreshing to see the loading sequence again.
Performance Tip
Making multiple API calls in separate useEffect hooks can slow your app. Consider combining related data into single endpoints. Instead of four calls for each stat, one /api/dashboard/stats call returns everything.
Quiz
1. Your DataFlow component shows revenue data but breaks when the API returns a 500 error. What's the most likely issue?
2. You want to use async/await in useEffect to load DataFlow user data. What's the correct approach?
3. Your DataFlow stats cards show empty values while the API loads, confusing users. What's the best solution?
API Calls with Axios
Take your API skills to the next level with Axios — automatic JSON parsing, request interceptors, and built-in error handling that makes fetch look primitive.