React Native Fetching Data from API
Most apps get their data from a server — product listings, user profiles, news articles, or weather. React Native uses the built-in fetch API to make HTTP requests. This topic covers the full pattern: making requests, showing a loading state, handling errors, and displaying the results.
How API Data Flows Into Your App
React Native App Server (API) ──────────────────────────────────────────────────────── 1. fetch() sends a request ────────► API receives request 2. Wait... (show spinner) API queries database 3. Response arrives ◄────────────── API sends JSON back 4. Parse JSON 5. Save to state 6. Render the list
The Basic fetch Pattern
fetch is a built-in JavaScript function. It returns a Promise — a value that will be available later. Use async/await to write asynchronous code that reads like normal sequential code.
import { useEffect, useState } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
export default function PostsScreen() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
loadPosts();
}, []);
async function loadPosts() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
setPosts(data);
} catch (err) {
setError('Failed to load posts. Check your connection.');
} finally {
setLoading(false);
}
}
if (loading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#007AFF" />
<Text style={{ marginTop: 12 }}>Loading...</Text>
</View>
);
}
if (error) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ color: 'red' }}>{error}</Text>
</View>
);
}
return (
<FlatList
data={posts}
keyExtractor={(item) => String(item.id)}
renderItem={({ item }) => (
<View style={{ padding: 16, borderBottomWidth: 1, borderColor: '#eee' }}>
<Text style={{ fontWeight: 'bold' }}>{item.title}</Text>
<Text style={{ color: '#666', marginTop: 4 }}>{item.body}</Text>
</View>
)}
/>
);
}
State machine for data fetching:
App opens
│
▼
loading = true → show spinner
│
├── success → loading = false, posts = [...] → show list
│
└── failure → loading = false, error = "msg" → show error
Understanding async/await
Without async/await (Promise chain): fetch(url) .then(res => res.json()) .then(data => setData(data)) .catch(err => setError(err)); With async/await (cleaner, reads top-to-bottom): const res = await fetch(url); const data = await res.json(); setData(data);
Both do the same thing. async/await is easier to read and debug. Always wrap it in try/catch to handle network failures.
HTTP Methods
fetch supports all HTTP methods. Use the method option to choose.
GET — Read Data (default)
const response = await fetch('https://api.example.com/users');
const users = await response.json();
POST — Create Data
const response = await fetch('https://api.example.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'New Post', body: 'Content here' }),
});
const created = await response.json();
PUT — Update Data
const response = await fetch('https://api.example.com/posts/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Updated Title' }),
});
DELETE — Remove Data
const response = await fetch('https://api.example.com/posts/1', {
method: 'DELETE',
});
Sending Auth Tokens
Protected APIs require an authentication token in the request header. Pass it through the Authorization header.
const token = 'eyJhbGciOiJIUzI1NiIs...'; // from login response
const response = await fetch('https://api.example.com/profile', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
Checking Response Status
fetch does not throw an error for HTTP error codes like 404 or 500. Check response.ok or response.status yourself.
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
// response.status = 404, 500, etc.
throw new Error(`Server error: ${response.status}`);
}
const data = await response.json();
response.ok is true when status is 200–299 response.ok is false when status is 400–599 Status codes you'll encounter: 200 → OK (success) 201 → Created (POST success) 400 → Bad Request (you sent wrong data) 401 → Unauthorized (bad/missing token) 403 → Forbidden (no permission) 404 → Not Found 500 → Server Error
Pagination — Loading More Items
APIs return data in pages to avoid sending thousands of records at once. Load the next page when the user scrolls to the bottom of the list.
const [posts, setPosts] = useState([]);
const [page, setPage] = useState(1);
const [loadingMore, setLoadingMore] = useState(false);
async function loadMore() {
if (loadingMore) return;
setLoadingMore(true);
const res = await fetch(`https://api.example.com/posts?page=${page}`);
const newPosts = await res.json();
setPosts(prev => [...prev, ...newPosts]); // append new items
setPage(page + 1);
setLoadingMore(false);
}
<FlatList
data={posts}
keyExtractor={(item) => String(item.id)}
renderItem={({ item }) => <PostItem {...item} />}
onEndReached={loadMore}
onEndReachedThreshold={0.3} // trigger when 30% from bottom
ListFooterComponent={loadingMore ? <ActivityIndicator /> : null}
/>
Scroll position diagram: ┌──────────────────────┐ │ Post 1 │ │ Post 2 │ │ Post 3 │ │ Post 4 │ │ Post 5 │← 30% from bottom triggers loadMore │ Post 6 │ │ Post 7 │ │ (loading spinner) │← ListFooterComponent └──────────────────────┘
Using axios Instead of fetch
axios is a popular alternative to fetch that automatically parses JSON, throws errors for non-2xx status codes, and makes request configuration cleaner.
npm install axios
import axios from 'axios';
const { data } = await axios.get('https://api.example.com/posts');
// data is already parsed — no .json() needed
// throws automatically on 4xx/5xx — no need to check response.ok
Summary
Use fetch inside useEffect with async/await to load data when a screen appears. Track loading and error states to show the right UI for every situation. Always check response.ok when using fetch. Use onEndReached on FlatList to implement infinite scroll pagination. For cleaner code, consider axios as an alternative to the raw fetch API.
