![]()
Từ những bài học đầu tiên tới bây giờ, chúng ta đã xây dựng được một ứng dụng có giao diện tuyệt đẹp, chuyển trang mượt mà. Nhưng có một sự thật: Toàn bộ dữ liệu hiển thị (như danh sách nhân vật, thông tin chi tiết) đều do chúng ta tự gõ cứng (hardcode) vào trong các file giao diện.
Trong thực tế, một bài viết hướng dẫn chiến thuật hay một danh sách tướng mới cập nhật không thể nằm chết trong code. Chúng phải được lấy về từ một máy chủ (Server). Khi có bản cập nhật mới, Server thay đổi dữ liệu, ứng dụng trên điện thoại của hàng triệu người dùng sẽ tự động cập nhật theo mà không cần phải tải lại app.
Để làm được điều đó, ứng dụng của bạn cần biết cách "nói chuyện" với Server. Và ngôn ngữ giao tiếp đó chính là API.
1. API là gì? Hiểu đơn giản nhất về RESTful API
API (Application Programming Interface) giống như một người bồi bàn trong nhà hàng.
- Bạn (Ứng dụng / Client) xem thực đơn và gọi món. Bạn đưa yêu cầu (Request) cho người bồi bàn.
- Người bồi bàn (API) mang yêu cầu đó vào nhà bếp (Server / Cơ sở dữ liệu).
- Nhà bếp nấu xong, đưa món ăn (Dữ liệu - Response) cho bồi bàn mang ra lại cho bạn.
Trong lập trình Mobile, chúng ta thường làm việc với RESTful API. Dữ liệu mà "bồi bàn" mang trả lại cho bạn thường được định dạng dưới chuẩn JSON (trông giống hệt một Object trong JavaScript).

Ví dụ, khi bạn gọi đến đường dẫn API cung cấp danh sách cẩm nang tướng, Server sẽ trả về một mảng JSON như sau:
[
{
"id": "1",
"name": "Lee Sin",
"role": "Đi Rừng",
"guide_outline": ["Bảng ngọc", "Trang bị", "Vòng rừng", "Combo cơ bản"]
},
{
"id": "2",
"name": "Ngộ Không",
"role": "Đường Trên / Đi Rừng",
"guide_outline": ["Bảng ngọc", "Trang bị", "Cách trade hit", "Giao tranh tổng"]
}
]
2. Hai công cụ mạnh mẽ: fetch vs Axios
Để ứng dụng React Native có thể đóng vai "khách hàng gọi món", chúng ta cần sử dụng các công cụ gửi HTTP Request. Có 2 lựa chọn phổ biến nhất:

Công cụ mặc định: fetch API
Đây là hàm có sẵn của JavaScript. Bạn không cần cài đặt thêm bất kỳ thư viện nào.
- Ưu điểm: Có sẵn, nhẹ, chuẩn Web/Native.
- Nhược điểm: Phải viết thêm bước ép kiểu dữ liệu từ Text sang JSON (response.json()), xử lý lỗi (catch error) đôi khi hơi dài dòng.
Công cụ chuyên nghiệp: Axios
Đây là thư viện bên thứ 3 (phải cài đặt bằng lệnh npm install axios). Các lập trình viên có kinh nghiệm và các dự án lớn thường sử dụng Axios.
- Ưu điểm: Cú pháp siêu ngắn gọn, tự động ép kiểu JSON, dễ dàng cài đặt các cấu hình chung (như tự động gắn Token đăng nhập vào mọi request), xử lý lỗi cực tốt.
Trong bài học này, để bạn nắm vững bản chất, chúng ta sẽ thực hành với fetch (bạn có thể dễ dàng chuyển sang Axios sau này vì tư duy là hoàn toàn giống nhau).
3. Thực hành: Gọi API lấy danh sách Cẩm nang tướng
Để gọi API và hiển thị lên màn hình, chúng ta sẽ cần kết hợp lại toàn bộ kiến thức đã học: useState (lưu dữ liệu), useEffect (kích hoạt việc lấy dữ liệu khi mở màn hình) và FlatList (hiển thị danh sách).
Chúng ta sẽ thiết lập 3 State (trạng thái) quan trọng nhất mà mọi ứng dụng gọi API đều phải có:
data: Biến lưu trữ dữ liệu lấy về.isLoading: Biến hiển thị vòng tròn xoay xoay (Loading) trong lúc chờ Server trả lời.error: Biến lưu thông báo lỗi nếu rớt mạng hoặc Server sập.
Hãy cùng xem đoạn code hoàn chỉnh dưới đây:
import React, { useState, useEffect } from 'react'
import { View, Text, FlatList, ActivityIndicator, StyleSheet, TouchableOpacity } from 'react-native'
export default function ChampionGuidesScreen() {
// 1. Khởi tạo 3 State cốt lõi
const [champions, setChampions] = useState([])
const [isLoading, setIsLoading] = useState(true) // Mặc định vừa vào màn hình là Loading
const [error, setError] = useState(null)
// 2. Viết hàm gọi API với async / await
const fetchChampions = async () => {
try {
// Bắt đầu gọi bồi bàn (Giả sử đây là một URL API có thật)
const response = await fetch('https://api.mockapi.io/v1/champions')
// Kiểm tra xem Server có trả về lỗi (404, 500) không
if (!response.ok) {
throw new Error('Không thể kết nối đến máy chủ!')
}
// Ép kiểu dữ liệu bồi bàn mang về thành JSON
const json = await response.json()
// Cất dữ liệu vào State
setChampions(json)
} catch (err) {
// Bắt lỗi nếu mất mạng hoặc sai URL
setError(err.message)
} finally {
// Dù thành công hay thất bại, cũng phải tắt vòng xoay Loading
setIsLoading(false)
}
}
// 3. Dùng useEffect để tự động gọi hàm khi vừa mở màn hình
useEffect(() => {
fetchChampions()
}, []) // Mảng rỗng = Chỉ chạy 1 lần duy nhất
// --- XỬ LÝ GIAO DIỆN (UI) ---
// Nếu đang tải dữ liệu
if (isLoading) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" color="#0984e3" />
<Text style={{ marginTop: 10 }}>Đang tải cẩm nang...</Text>
</View>
)
}
// Nếu có lỗi
if (error) {
return (
<View style={styles.center}>
<Text style={styles.errorText}>Đã xảy ra lỗi: {error}</Text>
<TouchableOpacity
style={styles.retryBtn}
onPress={() => {
setIsLoading(true)
setError(null)
fetchChampions()
}}
>
<Text style={{ color: 'white' }}>Thử lại</Text>
</TouchableOpacity>
</View>
)
}
// Nếu lấy dữ liệu thành công, đưa vào FlatList
return (
<View style={styles.container}>
<Text style={styles.header}>Thư Viện Cẩm Nang</Text>
<FlatList
data={champions}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.card}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.role}>Vai trò: {item.role}</Text>
<Text style={styles.outlineTitle}>Nội dung hướng dẫn ({item.guide_outline?.length || 8} phần):</Text>
<Text style={styles.outlineText}>{item.guide_outline?.join(' ➔ ')}</Text>
</View>
)}
/>
</View>
)
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#f5f6fa', padding: 15 },
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
header: { fontSize: 24, fontWeight: 'bold', marginBottom: 15, color: '#2d3436' },
card: { backgroundColor: '#fff', padding: 15, borderRadius: 10, marginBottom: 15, elevation: 3 },
name: { fontSize: 18, fontWeight: 'bold', color: '#0984e3' },
role: { fontSize: 14, color: '#636e72', marginTop: 4 },
outlineTitle: { fontSize: 13, fontWeight: '600', marginTop: 10 },
outlineText: { fontSize: 13, color: '#b2bec3', fontStyle: 'italic' },
errorText: { color: '#d63031', fontSize: 16, marginBottom: 10 },
retryBtn: { backgroundColor: '#d63031', padding: 10, borderRadius: 5 },
})
4. Phân tích kỹ thuật: Tại sao phải dùng async/await?
Trong đoạn code trên, bạn thấy chữ async và await xuất hiện. Đây là kiến thức cốt lõi của lập trình bất đồng bộ (Asynchronous) trong JavaScript.

Khi điện thoại yêu cầu dữ liệu từ Server, quá trình này có thể mất 0.1 giây hoặc lên tới 5 giây tùy thuộc vào tốc độ mạng.
Nếu không có await, JavaScript sẽ chạy tuột tuột xuống dòng bên dưới, lấy dữ liệu rỗng ném lên màn hình trước cả khi Server kịp trả lời. Từ khóa await buộc ứng dụng phải dừng lại và chờ cho đến khi "bồi bàn mang thức ăn ra", sau đó mới được phép thực thi các dòng code tiếp theo để nạp vào State.
Kết luận: API mở ra một thế giới mới
Một chức năng gọi API hoàn hảo luôn bao gồm 3 trạng thái: Loading, Lỗi, và Thành công. Bằng cách kết hợp useEffect để kích hoạt lệnh gọi, fetch/axios để giao tiếp, và useState để lưu kết quả, ứng dụng của bạn giờ đây đã có thể kết nối với toàn bộ thế giới Internet!
Tuy nhiên, API chỉ giúp lấy dữ liệu động từ Server. Đối với những thông tin tĩnh của cá nhân người dùng (như Giao diện sáng/tối, Lịch sử tìm kiếm gần nhất, hay Access Token đã đăng nhập), nếu ta cứ gọi lên Server để lấy thì sẽ rất lãng phí và chậm chạp.
Làm thế nào để lưu những thông tin đó vĩnh viễn ngay trên bộ nhớ cục bộ của chiếc điện thoại? Hẹn gặp lại bạn ở bài tiếp theo!