Nguyên nhân nằm ở mặc định của React Query: staleTime bằng 0, nên mọi dữ liệu vừa fetch xong đã bị coi là stale ngay; và query stale sẽ tự refetch khi mount mới, window lấy lại focus, hoặc mạng kết nối lại. gcTime mặc định 5 phút chỉ quyết định khi nào cache bị dọn, không ngăn refetch.
Cách xử lý theo thứ tự:
1. Mở DevTools của React Query xem query nào đang bắn — thường là những query dùng chung ở nhiều component nhưng bị queryKey khác nhau (truyền object có thứ tự field khác, hoặc tạo mới mỗi render) nên không dedupe được.
2. Đặt staleTime theo tốc độ thay đổi của dữ liệu, không đặt bừa toàn cục: danh mục sản phẩm, cấu hình, danh sách tỉnh thành có thể vài phút tới vài giờ; số dư ví, tồn kho thì giữ 0.
new QueryClient({
defaultOptions: {
queries: {
staleTime: 60_000,
gcTime: 5 * 60_000,
retry: (count, err) => (err instanceof HttpError && err.status < 500 ? false : count < 2),
},
},
})3. Chỉ tắt refetchOnWindowFocus cho những màn hình thật sự không cần dữ liệu mới. Tắt toàn cục là bỏ luôn tính năng giữ dữ liệu tươi khi user quay lại tab.
Cũng nên chỉnh retry: mặc định là 3 lần với backoff luỹ thừa, nên một lỗi 404/422 vẫn bị thử lại 3 lần — nhân số request lên vô ích và làm màn hình lỗi hiện chậm.