React Query (Tanstack Query)
React Query là một thư viện phổ biến được sử dụng để quản lý và lưu trữ dữ liệu bất đồng bộ trong các ứng dụng React. Thư viện cung cấp một cách đơn giản và hiệu quả để lấy dữ liệu, lưu trữ dữ liệu vào bộ nhớ cache và cập nhật dữ liệu trong các thành phần. Điều này giúp dễ dàng xử lý logic liên quan đến dữ liệu mà không cần phải sử dụng các giải pháp phức tạp quản lý trạng thái.
- Lấy dữ liệu: React Query cho phép bạn lấy dữ liệu từ các nguồn khác nhau như API hoặc các điểm cuối GraphQL, hỗ trợ sẵn việc xử lý các trường hợp thông thường như phân trang, lưu trữ cache và lấy dữ liệu ở nền.
- Lưu trữ cache và đồng bộ dữ liệu: Thư viện tự động lưu trữ cache các dữ liệu đã lấy, giảm thiểu việc gọi API không cần thiết. Nó cũng cung cấp các tính năng đồng bộ hóa để giữ cho dữ liệu cache luôn được cập nhật với dữ liệu từ máy chủ.
- Hủy bỏ truy vấn và gọi lại: React Query hỗ trợ các chiến lược hủy bỏ truy vấn và gọi lại để đảm bảo dữ liệu luôn mới mẻ khi cần thiết. Bạn có thể gọi lại thủ công hoặc thiết lập gọi lại tự động ở các khoảng thời gian cụ thể.
- Phụ thuộc truy vấn: Thư viện cho phép bạn tạo các truy vấn phụ thuộc, do đó khi dữ liệu của một truy vấn thay đổi, các truy vấn liên quan khác cũng tự động cập nhật.
- Mutations: React Query cũng xử lý các thay đổi dữ liệu (POST, PUT, DELETE) một cách dễ dàng và cung cấp một API đơn giản và nhất quán để cập nhật dữ liệu trên máy chủ và cập nhật bộ nhớ cache cục bộ.
- Tích hợp với các thành phần React: Thư viện cung cấp các hook như
useQueryvàuseMutation, giúp dễ dàng tích hợp logic lấy dữ liệu và cập nhật trực tiếp vào các thành phần React theo kiểu hàm.
Lưu ý trước khi học
React Query có cơ chế caching hơi khác một chút so với RTK Query, nên anh em đừng lấy logic của RTK Query rồi suy ngược lại React Query cũng giống vậy nhé.
Anh em hãy dành ra 1 phút để quên đi cách caching của RTK Query 😀😀
Một số khái niệm quan trọng
staleTime(default0ms): Thời gian data được cho là đã cũ. Khi get data xong thì sau một thời gian bạn quy định thì data nó sẽ tự cũ. Lưu ý cáistaletrên dev tool nó hiển thị là data của bạnstalevàactivecacheTime(default5601000ms tức 5 phút): Thời gian data sẽ bị xóa ra khỏi bộ nhớ đệm. Có thể data đã "cũ" nhưng nó chưa bị xóa ra khỏi bộ nhớ đệm vì bạn setstaleTime < cacheTime. Thường thì người ta sẽ setstaleTime < cacheTimeinactive: là khi data đó không còn component nào subcribe cả
const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
result là một object chứa một vài state rất quan trọng: status, fetchStatus,...
Những state về các khoảnh khắc của data
isLoadingorstatus === 'loading'- Query chưa có dataisErrororstatus === 'error'- Query xảy ra lỗiisSuccessorstatus === 'success'- Query thành công và data đã có sẵn
Những state về data
error- NếuisError === truethìerrorsẽ xuất hiện ở đâydata- NếuisSuccess === truethìdatasẽ xuất hiện ở đây
Đặc biệt là fetchStatus
isFetchingorfetchStatus === 'fetching'- Đang fetching API.isPausedorfetchStatus === 'paused'- Query muốn fetch API nhưng bị tạm dừng vì một lý do nào đó.fetchStatus === 'idle'- Query không làm gì cả
Nếu thấy quá rối vì quá nhiều trạng thái, sự khác nhau giữa status và fetchStatus là như thế nào?
Chỉ cần nhớ:
statuscho thông tindatacó hay khôngfetchStatuscho thông tin vềqueryFncó đang chạy hay không
Cơ chế caching
Một data mà đã stale thì khi gọi lại query của data đó, nó sẽ fetch lại api. Nếu không stale thì không fetch lại api.
Một data mà bị xóa khỏi bộ nhớ (tức là quá thời gian cacheTime) thì khi gọi lại query của data đó, nó sẽ fetch lại api. Nếu còn chưa bị xóa khỏi bộ nhớ nhưng đã stale thì nó sẽ trả về data cached và fetch api ngầm, sau khi fetch xong nó sẽ update lại data cached và trả về data mới cho bạn.
Caching là một vòng đời của:
- Query Instance có hoặc không cache data
- Fetch ngầm (background fetching)
- Các inactive query
- Xóa cache khỏi bộ nhớ (Garbage Collection)
Một ví dụ như thế này cho anh em dễ hiều:
Giả sử chúng ta dùng cacheTime mặc định là 5 phút và staleTime là 0.
function A() {
const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
}
function B() {
const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
}
function C() {
const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
}
Acomponent được mount- Vì không có query nào với
['todos']trước đó, nó sẽ fetch data - Khi fetch xong, data sẽ được cache dưới key là
['todos'] - hook đánh dấu data là
stale(cũ) vì sau0s - Bây giờ thì
Bcomponent được mount ở một nơi nào đó - Vì cache data
['todos']đã có trước đó, data từ cache sẽ trả về ngay lập tức cho componentB - Vì cache data
['todos']được cho là đãstalenên nó sẽ fetch api tại componentB - Không quan trọng function
fetchTodosởAvàBcó giống nhau hay không, việc fetch api tạiBsẽ cập nhật tất cả các state query liên quan củaBvàAvì 2 component cùng key => cùng subcribe đến một data - Khi fetch thành công, cache data
['todos']sẽ được cập nhật, cả 2 comonentAvàBcũng được cập nhật data mới - Bây giờ thì
AvàBunmount, không còn sử dụng nữa, không còn subcribe đến cache data['todos']nữa nên data['todos']bị cho làinactive - Vì
inactivenêncacheTimesẽ bắt đầu đếm ngược 5 phút - Trước khi
cacheTimehết thì ôngCcomopnent được mount. cache data['todos']được trả về ngay lập tức choCvàfetchTodossẽ chạy ngầm. Khi nó hoàn thành thì sẽ cập nhật lại cache với data mới. - Cuối cùng thì
Cunmount - Không còn ai subcribe đến cache data
['todos']trong 5 phút tiếp theo nữa và cache data['todos']bị xóa hoàn toàn.




