Giới thiệu vấn đề

Khi xây dựng một Ecommerce Dashboard phức tạp, việc gọi API liên tục sẽ gây tốn băng thông, làm chậm trải nghiệm người dùng và tăng chi phí server. Thông thường, các component React sẽ thực hiện fetch mỗi khi chúng được mount, dẫn đến việc tải lại cùng một dữ liệu nhiều lần. Bài viết này sẽ hướng dẫn cách kết hợp Tanstack QueryZustand để tối ưu hoá số lần fetch, đồng thời giữ cho trạng thái UI luôn đồng bộ.

Tại sao Tanstack Query lại quan trọng

Tanstack Query (trước đây là React Query) cung cấp một lớp trừu tượng mạnh mẽ cho việc quản lý dữ liệu bất đồng bộ. Nó tự động cache kết quả, cung cấp các chiến lược stale-while-revalidate, và giảm thiểu các request trùng lặp. Khi cấu hình đúng, mỗi query duy nhất chỉ được thực hiện một lần trong khoảng thời gian staleTime được định nghĩa.

Kết hợp Tanstack Query với Zustand để quản lý trạng thái phụ trợ

Bước 1: Cấu hình Tanstack Query Client

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

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5 phút
      cacheTime: 30 * 60 * 1000, // 30 phút
      refetchOnWindowFocus: false,
    },
  },
});

export default function App({ children }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}

Bước 2: Tạo store Zustand cho meta cache

Zustand giúp lưu trữ các thông tin phụ trợ như thời gian fetch cuối cùng của mỗi query. Khi một query được thực hiện thành công, chúng ta cập nhật timestamp trong store, từ đó có thể quyết định có nên bỏ qua fetch trong các component con.

import create from 'zustand';
import { devtools } from 'zustand/middleware';

export const useCacheStore = create(devtools((set) => ({
  // lưu thời gian fetch cuối cùng cho mỗi key
  timestamps: {},
  setTimestamp: (key) => set((state) => ({
    timestamps: { ...state.timestamps, [key]: Date.now() },
  })),
})));

Bước 3: Sử dụng trong component

Trong component, chúng ta gọi useQuery như bình thường, đồng thời ghi lại timestamp khi dữ liệu trả về. Khi component được mount lại, Tanstack Query sẽ kiểm tra staleTime và tự động trả về dữ liệu cache nếu chưa hết hạn, tránh việc gửi request mới.

import { useQuery } from '@tanstack/react-query';
import { useCacheStore } from '../store/cache';

function ProductList() {
  const { setTimestamp } = useCacheStore();
  const { data, isLoading } = useQuery(['products'], fetchProducts, {
    onSuccess: () => setTimestamp('products'),
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <ul>
      {data.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

Chiến lược tối ưu hoá fetch bổ sung

  • Prefetch dữ liệu quan trọng: Sử dụng queryClient.prefetchQuery trong quá trình navigation để dữ liệu đã sẵn sàng khi người dùng chuyển trang.
  • Batching các request: Khi cần lấy nhiều tài nguyên đồng thời, gộp chúng vào một endpoint duy nhất hoặc dùng Promise.all để giảm số lần round‑trip.
  • Sử dụng select trong query: Chỉ lấy những trường cần thiết, giảm kích thước payload.
  • Invalidate thông minh: Gọi queryClient.invalidateQueries chỉ khi dữ liệu thực sự thay đổi (ví dụ: sau khi tạo, cập nhật hoặc xóa sản phẩm).

Kết luận

Bằng việc cấu hình staleTime hợp lý, kết hợp Tanstack Query với Zustand để lưu trữ meta cache, chúng ta có thể giảm đáng kể số lần fetch trong các dashboard phức tạp mà không làm mất tính nhất quán của UI. Khi áp dụng các chiến lược bổ sung như prefetch và batching, hiệu suất sẽ được nâng cao hơn nữa.

Để nắm vững toàn bộ quy trình xây dựng một Ecommerce Dashboard fullstack, Tham khảo khóa học "[Full Course] Ecommerce Dashboard Fullstack Clone" tại đây.