Command Palette

Search for a command to run...

Cách gọi API trong React Native: Hướng dẫn Fetch Data và xử lý JSON

Cách gọi API trong React Native

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.

  1. 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.
  2. Người bồi bàn (API) mang yêu cầu đó vào nhà bếp (Server / Cơ sở dữ liệu).
  3. 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).

RESTful API là gì?

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:

Lựa chọn fetch hay Axios trong ứng dụng React Native

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ó:

  1. data: Biến lưu trữ dữ liệu lấy về.
  2. isLoading: Biến hiển thị vòng tròn xoay xoay (Loading) trong lúc chờ Server trả lời.
  3. 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ữ asyncawait 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.

Asynchronous 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!

Bài viết liên quan

Cách dùng FlatList trong React Native: Hiển thị danh sách không giật lag

Giải quyết bài toán hiển thị danh sách dữ liệu lớn với FlatList và SectionList. Hướng dẫn tối ưu hóa bộ nhớ và tốc độ cuộn (scroll) mượt mà.

Event Handling trong React Native: Sự khác biệt giữa Pressable và TouchableOpacity

Hướng dẫn cách bắt sự kiện chạm, nhấn trong React Native. Tìm hiểu vì sao không nên dùng thẻ Button và cách thay thế bằng Pressable, TouchableOpacity.

Hướng dẫn cài đặt môi trường React Native & Expo trong 15 phút

Bạn sợ cài đặt môi trường lập trình mobile phức tạp? Hướng dẫn chi tiết cách dùng Expo Go, Node.js để chạy app React Native ngay lập tức.

Stack Navigation trong Expo Router: Chuyển trang & Truyền tham số

Hướng dẫn chi tiết cách chuyển trang và truyền tham số bằng Stack Navigation trong React Native, sử dụng Expo Router.