Quay lại blog
tips8 phút đọc

Quản lý State trong React 2026: Zustand, TanStack Query và URL State

Chọn đúng công cụ quản lý state trong React — TanStack Query cho server state, Zustand cho global UI state, URL cho filter, useState cho local state.

V
Bởi Ventra Rocket
·Đăng ngày 10 tháng 2, 2026
#React#Zustand#TanStack Query#State Management#Frontend

Quản lý state trong React là về việc dùng đúng công cụ cho từng loại state.

Phân loại State

| Loại State | Ví dụ | Công cụ tốt nhất | |-----------|-------|-----------------| | Server state | API data, cache | TanStack Query | | Global UI state | modal, theme | Zustand | | Local UI state | input, toggle | useState | | URL state | filter, phân trang | searchParams | | Form state | validation | React Hook Form |

1. Server State với TanStack Query

function useProducts(category: string) {
  return useQuery({
    queryKey: ["products", category],
    queryFn: () => fetchProducts(category),
    staleTime: 5 * 60 * 1000,  // 5 phút
  });
}

function useCreateProduct() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: createProduct,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["products"] });
    },
  });
}

2. Global UI State với Zustand

import { create } from "zustand";
import { persist } from "zustand/middleware";

const useUIStore = create()(
  persist(
    (set) => ({
      theme: "dark" as "light" | "dark",
      toggleTheme: () =>
        set((state) => ({ theme: state.theme === "dark" ? "light" : "dark" })),
    }),
    { name: "ui-store" }
  )
);

3. URL State cho Filter

function useProductFilters() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const category = searchParams.get("category") ?? "all";
  const page = Number(searchParams.get("page") ?? 1);

  const updateFilter = useCallback(
    (key: string, value: string) => {
      const params = new URLSearchParams(searchParams.toString());
      params.set(key, value);
      if (key !== "page") params.set("page", "1");
      router.push(`?${params.toString()}`, { scroll: false });
    },
    [router, searchParams]
  );

  return { category, page, updateFilter };
}

4. Form State với React Hook Form + Zod

const schema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
});

function ContactForm() {
  const { register, handleSubmit, formState: { errors } } =
    useForm({ resolver: zodResolver(schema) });
  return (
    <form onSubmit={handleSubmit(async (data) => submitContact(data))}>
      <input {...register("email")} />
      {errors.email && <span>{errors.email.message}</span>}
      <button type="submit">Gửi</button>
    </form>
  );
}

Anti-Patterns cần tránh

  • useEffect cho derived state — tính trực tiếp trong render
  • Context cho high-frequency updates — Context re-render tất cả consumers
  • useState cho server data — dùng TanStack Query
  • Redux cho app nhỏ — quá nhiều boilerplate

Kết luận

TanStack Query cho server state, Zustand cho global UI, useState cho local, URL cho shareable state. Combination này giảm re-render và boilerplate.

Bài viết liên quan

Quản lý State trong React 2026: Zustand, TanStack Query và URL State | Ventra Rocket