Zustand Async: 5 Cách Xử Lý Bất Đồng Bộ Hiệu Quả Trong React
Mọi ứng dụng React thực tế đều phải giao tiếp với API bất đồng bộ. Nếu quản lý kém, ứng dụng dễ rơi vào các vấn đề như UI bị đơ, race condition, rò rỉ bộ nhớ, hay hiển thị dữ liệu cũ. Zustand giải quyết bài toán này với một cú pháp cực kỳ đơn giản — định nghĩa async actions trực tiếp trong store, không cần middleware phức tạp như Redux Thunk hay Saga.

Trung Vũ Hoàng
Tác giả
1. Tại Sao Cần Quản Lý Async State Tập Trung?
Mỗi yêu cầu API cần theo dõi ít nhất 3 trạng thái: loading, success, error. Nếu logic này bị phân tán khắp các component, codebase nhanh chóng trở nên khó đọc và khó debug. Tập trung async state vào Zustand store giúp:
Logic fetch tách hoàn toàn khỏi UI component.
Nhiều component dùng chung một nguồn dữ liệu duy nhất.
Dễ xử lý race condition và hủy request khi component unmount.
2. Cách 1 — Store Async Cơ Bản
Async action trong Zustand chỉ là một hàm async thông thường bên trong store:
import { create } from 'zustand';
const useUserStore = create((set) => ({
user: null,
isLoading: false,
error: null,
fetchUser: async (userId) => {
set({ isLoading: true, error: null });
try {
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) throw new Error('Không thể tải dữ liệu!');
const data = await res.json();
set({ user: data, isLoading: false });
} catch (err) {
set({ error: err.message, isLoading: false });
}
},
}));3. Cách 2 — Dùng Trong Component Với Selector
Chỉ subscribe đúng mảnh state cần thiết để tránh re-render thừa:
const UserProfile = ({ userId }) => {
const { user, isLoading, error, fetchUser } = useUserStore(state => ({
user: state.user,
isLoading: state.isLoading,
error: state.error,
fetchUser: state.fetchUser,
}));
useEffect(() => {
fetchUser(userId);
}, [userId]);
if (isLoading) return <p>Đang tải...</p>;
if (error) return <p>Lỗi: {error}</p>;
return <div>{user?.name}</div>;
};4. Cách 3 — Song Song Nhiều Request
Dùng Promise.all bên trong action để fetch nhiều nguồn cùng lúc:
fetchDashboard: async () => {
set({ isLoading: true });
try {
const [stats, users, orders] = await Promise.all([
fetch('/api/stats').then(r => r.json()),
fetch('/api/users').then(r => r.json()),
fetch('/api/orders').then(r => r.json()),
]);
set({ stats, users, orders, isLoading: false });
} catch (err) {
set({ error: err.message, isLoading: false });
}
},5. Cách 4 — Optimistic Update
Cập nhật UI ngay lập tức, rollback nếu API thất bại:
addItem: async (item) => {
// Cập nhật UI trước
set(state => ({ items: [...state.items, item] }));
try {
await fetch('/api/items', { method: 'POST', body: JSON.stringify(item) });
} catch (err) {
// Rollback nếu lỗi
set(state => ({ items: state.items.filter(i => i.id !== item.id) }));
}
},6. Cách 5 — Hủy Request Khi Không Cần Nữa
Dùng AbortController để hủy fetch khi component unmount hoặc userId thay đổi:
fetchUser: async (userId, signal) => {
set({ isLoading: true });
try {
const res = await fetch(`/api/users/${userId}`, { signal });
const data = await res.json();
set({ user: data, isLoading: false });
} catch (err) {
if (err.name !== 'AbortError') {
set({ error: err.message, isLoading: false });
}
}
},
// Trong component:
useEffect(() => {
const controller = new AbortController();
fetchUser(userId, controller.signal);
return () => controller.abort(); // Hủy khi unmount
}, [userId]);7. Tối Ưu Với Middleware
devtools: Tích hợp Redux DevTools — debug async actions dễ dàng.
immer: Cú pháp cập nhật state mutable nhưng vẫn immutable bên dưới.
persist: Lưu state vào localStorage, phục hồi khi reload trang.
Tổng Kết
Zustand biến việc xử lý async trong React trở nên gọn nhẹ và tường minh hơn bất kỳ giải pháp nào khác:
Cơ bản: async function trực tiếp trong store.
Hiệu năng: Selector để tránh re-render thừa.
Song song: Promise.all trong action.
UX tốt: Optimistic update + rollback.
An toàn: AbortController để tránh memory leak.
Câu hỏi thường gặp
Bài viết liên quan

Zustand Async: 5 Cách Xử Lý Bất Đồng Bộ Hiệu Quả Trong React
Mọi ứng dụng React thực tế đều phải giao tiếp với API bất đồng bộ. Nếu quản lý kém, ứng dụng dễ rơi vào các vấn đề như UI bị đơ, race condition, rò rỉ bộ nhớ, hay hiển thị dữ liệu cũ. Zustand giải quyết bài toán này với một cú pháp cực kỳ đơn giản — định nghĩa async actions trực tiếp trong store, không cần middleware phức tạp như Redux Thunk hay Saga.

Promise Nâng Cao: all, allSettled, race, any — Khi Nào Dùng Cái Nào?
Nếu bạn chỉ dùng async/await tuần tự, ứng dụng của bạn đang bỏ phí tiềm năng hiệu suất — mỗi request phải chờ request trước xong mới bắt đầu. Các phương thức tĩnh của Promise (all, allSettled, race, any) cho phép bạn điều phối nhiều tác vụ bất đồng bộ song song theo chiến lược phù hợp từng bài toán.

Virtualization Trong React: Kỹ Thuật Xử Lý 100.000 Dòng Dữ Liệu Không Giật Lag
Bạn có một danh sách 50.000 dòng cần hiển thị trong React. Nếu render tất cả cùng lúc, trình duyệt sẽ phải dựng hàng chục nghìn DOM node — CPU và RAM rên rỉ, UI giật lag nghiêm trọng, và điểm Core Web Vitals lao dốc. Virtualization (hay Windowing) giải quyết bài toán này bằng một nguyên lý đơn giản mà cực kỳ hiệu quả.