Command Palette

Search for a command to run...

Tối ưu việc gọi API trong React Native bằng React Query

Tối ưu việc gọi API trong React Native bằng React Query

Ở những bài trước, chúng ta đã học cách gọi API bằng fetch kết hợp với useEffectuseState. Rồi sau đó, chúng ta học cách dùng Redux/Zustand để quản lý dữ liệu trên toàn ứng dụng. Rất nhiều lập trình viên nghĩ rằng: "À, vậy mình sẽ gọi API, sau đó lấy dữ liệu nhét vào Redux để dùng chung cho sướng!"

Nhưng sự thật là: Đó là một sai lầm kiến trúc khiến ứng dụng của bạn trở nên chậm chạp và chứa đầy mã rác (boilerplate)!

Tại sao lại như vậy? Và làm cách nào những ứng dụng hàng đầu có thể vuốt danh sách mượt mà, lưu trữ dữ liệu đệm (cache) thông minh ngay cả khi mạng chập chờn? Chào mừng bạn đến với kỷ nguyên của React Query (nay đã đổi tên thành TanStack Query).

1. Sự khác biệt sống còn: Client State vs Server State

Để hiểu tại sao Redux hay Zustand lại "bất lực" trước API, bạn cần phân biệt được 2 loại trạng thái (State) trong ứng dụng:

  • Client State (Trạng thái máy khách): Là những dữ liệu hoàn toàn do người dùng điện thoại kiểm soát. Ví dụ: Người dùng đang chọn tab nào, modal nào đang mở, giỏ hàng có bao nhiêu món. Dữ liệu này đồng bộ, nhanh chóng và không bao giờ bị trễ. ➡️ Đây là sân chơi của Zustand / Redux.
  • Server State (Trạng thái máy chủ): Là dữ liệu bạn vay mượn từ cơ sở dữ liệu trên mây. Ví dụ: Danh sách cẩm nang tướng, bình luận bài viết, thông tin trận đấu. Dữ liệu này bất đồng bộ, có độ trễ (phụ thuộc mạng), và có thể bị người khác thay đổi bất cứ lúc nào (out of date). ➡️ Việc nhét dữ liệu này vào Redux là cực kỳ vất vả.

Nếu dùng useEffect và Redux để xử lý Server State, với mỗi một API, bạn sẽ phải tự tay viết logic cho 3 biến: data, isLoading, isError. Nếu bạn có 50 API khác nhau, bạn sẽ chìm trong một biển code lặp đi lặp lại.

React Query ra đời để "giết chết" mớ hỗn độn đó.

2. React Query: Giải cứu ứng dụng khỏi useEffect

Hãy xem cách React Query đơn giản hóa việc lấy danh sách cẩm nang các vị tướng phức tạp (như Ahri, Akali, hay Aatrox) chỉ với một Hook duy nhất mang tên useQuery.

Bước 1: Cài đặt thư viện

npm install @tanstack/react-query

Bước 2: Bọc ứng dụng trong Provider

Ở file app/_layout.tsx hoặc App.js

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

// Khởi tạo một trạm quản lý API
const queryClient = new QueryClient()

export default function RootLayout() {
  return <QueryClientProvider client={queryClient}>{/* Các màn hình Expo Router của bạn ở đây */}</QueryClientProvider>
}

Bước 3: Sử dụng useQuery thay cho useEffect

Hãy xem phép màu xảy ra (ở màn hình Danh Sách). Toàn bộ logic khai báo State, gọi hàm, bắt lỗi, tắt bật Loading giờ thu bé lại chỉ bằng chừng này code:

import { View, Text, FlatList, ActivityIndicator } from 'react-native'
import { useQuery } from '@tanstack/react-query'

// Hàm fetch API thuần túy (Không dính dáng gì đến UI)
const fetchChampions = async () => {
  const response = await fetch('https://api.example.com/champions')
  if (!response.ok) throw new Error('Lỗi mạng!')
  return response.json()
}

export default function ChampionList() {
  // Sức mạnh của React Query: Tự động quản lý mọi trạng thái
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ['champions'], // Tên định danh cho API này (như chiếc thẻ tên)
    queryFn: fetchChampions, // Hàm sẽ được thực thi
  })

  if (isLoading) return <ActivityIndicator size="large" />
  if (isError) return <Text>Đã xảy ra lỗi: {error.message}</Text>

  return (
    <FlatList
      data={data}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <Text>
          {item.name} - {item.role}
        </Text>
      )}
    />
  )
}

Không cần useState! Không cần useEffect! Bạn chỉ việc khai báo bạn muốn gì, React Query sẽ làm mọi thứ dưới nền.

3. Bộ nhớ đệm (Caching) & Stale-While-Revalidate

Tính năng "ăn tiền" nhất của React Query không phải là viết code ngắn hơn, mà là cơ chế Cache tự động.

Bộ nhớ đệm (Caching) trong React Query

Khi bạn dùng useQuery lấy danh sách tướng (có queryKey['champions']), nó sẽ tự động lưu mảng dữ liệu đó vào bộ nhớ đệm (Cache) của điện thoại.

Kịch bản hoàn hảo:

  1. Lần đầu bạn mở app, bạn phải chờ 1 giây để xem danh sách.
  2. Bạn bấm sang xem trang Chi tiết của Akali.
  3. Bạn nhấn "Back" (Quay lại) trang Danh sách.
  4. Kết quả: Danh sách hiện ra NGAY LẬP TỨC không có lấy 1 mili-giây delay!

React Query sử dụng chiến lược Stale-while-revalidate: Nó sẽ lập tức lấy dữ liệu cũ trong Cache ra cho bạn xem trước để trải nghiệm mượt mà, đồng thời nó âm thầm cử một request chạy ngầm dưới nền (background) lên Server. Nếu trên Server có tướng mới vừa ra mắt, nó sẽ tự động thay thế giao diện mới mà không làm giật app!

4. useMutation: Gửi dữ liệu lên Server

Nếu useQuery sinh ra để đọc dữ liệu (GET), thì useMutation là vũ khí để bạn Ghi, Sửa, Xóa dữ liệu (POST, PUT, DELETE).

Ví dụ: Bạn muốn đánh dấu một bài hướng dẫn là "Yêu thích".

import { useMutation, useQueryClient } from '@tanstack/react-query'
import { TouchableOpacity, Text, Alert } from 'react-native'

const addToFavorites = async (championId) => {
  const response = await fetch('https://api.example.com/favorites', {
    method: 'POST',
    body: JSON.stringify({ id: championId }),
  })
  return response.json()
}

export default function FavoriteButton({ championId }) {
  const queryClient = useQueryClient()

  // Khai báo Mutation
  const mutation = useMutation({
    mutationFn: addToFavorites,
    onSuccess: () => {
      Alert.alert('Thành công!', 'Đã thêm vào danh sách yêu thích.')
      // Quan trọng: Ra lệnh cho API danh sách yêu thích chạy lại để cập nhật UI
      queryClient.invalidateQueries({ queryKey: ['favoriteList'] })
    },
  })

  return (
    <TouchableOpacity
      onPress={() => mutation.mutate(championId)}
      disabled={mutation.isPending} // Vô hiệu hóa nút khi đang gửi API
    >
      <Text>{mutation.isPending ? 'Đang lưu...' : '❤️ Yêu thích'}</Text>
    </TouchableOpacity>
  )
}

Kết luận: Cần kết hợp hài hòa Client State và Server State

Sự kết hợp giữa Zustand (Quản lý Client State)React Query (Quản lý Server State) hiện đang là "Tech Stack" vô địch trong React Native. Nó gạt bỏ hàng ngàn dòng code dư thừa, giúp bạn tập trung 100% vào việc làm ra các tính năng xuất sắc.

Đến giờ này, ứng dụng của bạn đã thực sự là một sản phẩm hoàn thiện về mặt kết nối dữ liệu. Bạn đã biết gọi API, hiển thị danh sách, lưu trữ offline và tối ưu hóa bộ nhớ đệm. Tuy nhiên, một ứng dụng Mobile thực thụ không thể chỉ có màn hình và chữ! Để cạnh tranh trên App Store, ứng dụng của bạn phải biết tận dụng phần cứng của điện thoại.

Hãy thắt dây an toàn và chuẩn bị tiến vào những bài học thú vị phía trước nhé!

Bài viết liên quan

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

Hướng dẫn chi tiết cách gọi API trong React Native. Nắm vững kỹ thuật sử dụng fetch, axios kết hợp với useEffect, useState để tải và hiển thị dữ liệu server mượt mà.

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.

Quản lý Global State trong React Native: Redux Toolkit hay Zustand?

Giải thích chi tiết khái niệm Global State và Prop Drilling. Hướng dẫn cách cài đặt, sử dụng Redux Toolkit và Zustand để quản lý luồng dữ liệu React Native.

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à.