Chuyển tới nội dung chính

State Management

Tổng quan

Hệ thống sử dụng 2 lớp state:

LớpCông cụDùng cho
Client stateRedux Toolkit (slices)Auth, store selection, UI preferences
Server stateRTK QueryAPI data, caching, auto-refetch

Tại sao RTK Query?

  • Automatic caching — không cần quản lý loading, error, data thủ công
  • Auto-refetch — khi storeId thay đổ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 / useLazyXxxQuery cho 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 useState cho loading khi đã có RTK Query
  • ❌ Không dùng getSelectedStoreId() trong component (chỉ dùng ngoài React)