State Management
Tổng quan
Hệ thống sử dụng 2 lớp state:
| Lớp | Công cụ | Dùng cho |
|---|---|---|
| Client state | Redux Toolkit (slices) | Auth, store selection, UI preferences |
| Server state | RTK Query | API data, caching, auto-refetch |
Tại sao RTK Query?
- Automatic caching — không cần quản lý
loading,error,datathủ công - Auto-refetch — khi
storeIdthay đổi, data tự fetch lại - Code generation — chỉ cần khai báo endpoint, RTK Query tạo hook tự động
- Optimistic updates — cập nhật UI trước khi API trả về
Store setup
Mỗi MFE có store riêng (không share store giữa MFEs):
// apps/<module>/src/store/index.ts
import { configureStore } from "@reduxjs/toolkit";
import { baseApi } from "@/config/baseApi";
export const store = configureStore({
reducer: {
[baseApi.reducerPath]: baseApi.reducer,
// ... other slices
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(baseApi.middleware),
});
setupListeners(store.dispatch); // Enable auto-refetch
RTK Query Service
API khai báo trong src/services/*.service.ts:
// src/services/product.service.ts
import { baseApi } from "@/config/baseApi";
import type { CommonResponse, PaginationResponse } from "@vppos/core/types";
import type { Product } from "@/types/product.type";
export const productApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getProducts: builder.query<PaginationResponse<Product>, { page: number }>({
query: ({ page }) => ({
url: "/products",
params: { page },
}),
}),
createProduct: builder.mutation<CommonResponse<Product>, FormData>({
query: (body) => ({
url: "/products",
method: "POST",
body,
}),
}),
}),
});
export const { useGetProductsQuery, useCreateProductMutation } = productApi;
Container store (đặc biệt)
Container host quản lý global state cho toàn bộ hệ thống:
// Auth state — đồng bộ qua tokenSync
interface AuthState {
isAuthenticated: boolean;
isInitialized: boolean;
user: User | null;
token: string | null;
}
// Store selection — persist qua localStorage
interface StoreSelectionState {
selectedStoreId: string | null;
selectedStore: Store | null;
}
Quy tắc
Dos
- ✅ Dùng
useXxxQuery/useLazyXxxQuerycho mọi API call - ✅ Dùng
useStoreSelection()hook trong component (reactive) - ✅ Dùng
getApiErrorMessage(err)để lấy error message
Don'ts
- ❌ Không gọi
fetch()/axios()trực tiếp trong component - ❌ Không dùng
useStatecho loading khi đã có RTK Query - ❌ Không dùng
getSelectedStoreId()trong component (chỉ dùng ngoài React)