![]()
Ở 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 useEffect và useState. 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.

Khi bạn dùng useQuery lấy danh sách tướng (có queryKey là ['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:
- Lần đầu bạn mở app, bạn phải chờ 1 giây để xem danh sách.
- Bạn bấm sang xem trang Chi tiết của Akali.
- Bạn nhấn "Back" (Quay lại) trang Danh sách.
- 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) và 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é!