Quản lý trạng thái toàn cục trong ứng dụng React
Khi xây dựng các ứng dụng React hiện đại, một trong những câu hỏi phổ biến mà các đội ngũ phát triển thường gặp phải là:
Làm thế nào để quản lý trạng thái cần được truy cập bởi các thành phần lồng nhau sâu?
Rất dễ để sử dụng API Context của React hoặc bắt đầu truyền props, nhưng khi ứng dụng ngày càng phát triển, những cách tiếp cận này thường dẫn đến các nút thắt về hiệu suất và mã nguồn phức tạp.
Trong một trong những dự án gần đây của chúng tôi, chúng tôi đã gặp phải tình huống này:
React Queryđã được sử dụng để quản lý trạng thái từ máy chủ (dữ liệu dựa trên API).Zustandđược sử dụng cho một số nhu cầu phía khách hàng (lưu trữ cục bộ, hoàn tác/khôi phục).- Các thư viện bên ngoài mang theo các nhà cung cấp Context riêng.
Bây giờ chúng tôi cần một cách để thêm một trạng thái cục bộ toàn cục — có thể truy cập từ bất kỳ cấp độ nào của cây thành phần.
Nguyên tắc cơ bản: Các loại trạng thái khác nhau
Trước khi quyết định cách quản lý trạng thái, điều quan trọng là phân tách trạng thái từ máy chủ và trạng thái từ phía khách hàng:
Trạng thái từ máy chủ: Dữ liệu đến từ các API (người dùng, bài viết, cài đặt). Tốt nhất là sử dụng React Query, cho phép chúng ta có tính năng lưu trữ, cập nhật nền và vô hiệu hóa ngay lập tức.
Trạng thái từ phía khách hàng: Dữ liệu chỉ tồn tại trong ứng dụng (các công tắc UI, sở thích cục bộ, trạng thái biểu mẫu tạm thời). Đây là nơi mà các công cụ như Zustand tỏa sáng.
Tại sao không dùng Context cho mọi thứ?
React Context có chỗ đứng của nó — những thứ như chủ đề, địa phương hóa và xác thực là những trường hợp tuyệt vời. Nhưng đối với trạng thái thường xuyên thay đổi, Context mang lại hai vấn đề:
- Hiệu suất – bất kỳ cập nhật nào cũng có thể làm cho toàn bộ cây người tiêu dùng được vẽ lại.
- Khả năng mở rộng – khi trạng thái gia tăng, nhiều Context nhanh chóng trở nên khó bảo trì.
Tại sao Zustand hoạt động tốt hơn ở đây
Zustand nhẹ, có khả năng mở rộng và tránh được những cạm bẫy của Context. Mỗi thành phần chỉ có thể đăng ký phần của cửa hàng mà nó quan tâm, do đó các lần vẽ lại được cô lập.
Dưới đây là một ví dụ đơn giản:
typescript
// store/globalStore.ts
import { create } from "zustand";
type GlobalState = {
theme: "light" | "dark";
setTheme: (t: "light" | "dark") => void;
isSidebarOpen: boolean;
toggleSidebar: () => void;
};
export const useGlobalStore = create<GlobalState>((set) => ({
theme: "light",
setTheme: (t) => set({ theme: t }),
isSidebarOpen: false,
toggleSidebar: () => set((s) => ({ isSidebarOpen: !s.isSidebarOpen })),
}));
Sử dụng ở bất kỳ đâu trong ứng dụng:
typescript
import { useGlobalStore } from "@/store/globalStore";
function SidebarToggle() {
const isOpen = useGlobalStore((s) => s.isSidebarOpen);
const toggle = useGlobalStore((s) => s.toggleSidebar);
return (
<button onClick={toggle}>
{isOpen ? "Đóng Sidebar" : "Mở Sidebar"}
</button>
);
}
Không cần truyền props. Không có vẽ lại không cần thiết. Chỉ cần trạng thái sạch sẽ và dễ dự đoán.
Thực tiễn tốt nhất và những suy nghĩ cuối cùng
Khi các ứng dụng React mở rộng, các quyết định quản lý trạng thái sẽ tích lũy. Cách tiếp cận tốt nhất không phải là giới thiệu các công cụ mới mỗi khi có một nhu cầu mới xuất hiện, mà là tận dụng các mẫu hiện có một cách khôn ngoan:
- Sử dụng React Query cho trạng thái từ máy chủ – đừng tự làm lại quản lý trạng thái không đồng bộ.
- Sử dụng Zustand cho trạng thái cục bộ/toàn cục – có khả năng mở rộng, hiệu suất và dễ áp dụng.
- Giữ Context ở mức tối thiểu – chỉ cho những mối quan tâm thực sự như chủ đề hoặc i18n.
- Chia nhỏ các cửa hàng Zustand thành các phần nhỏ – tránh một "cửa hàng thần thánh" bằng cách phân tách các mối quan tâm (phần UI, phần sở thích, v.v.).
Sự kết hợp này giữ cho ứng dụng nhất quán, hiệu suất cao và dễ bảo trì cho các kỹ sư.
✨ Tôi rất muốn nghe từ các kỹ sư khác — bạn quản lý trạng thái toàn cục trong ứng dụng React của bạn như thế nào?