Fluxor là một thư viện giúp quản lý trạng thái (State Management) một cách chặt chẽ và dễ dự đoán. Để hiểu cách nó hoạt động, chúng ta hãy nhìn vào luồng dữ liệu sau:
1. Luồng hoạt động
Hiểu đơn giản:
- State (Trạng thái): Là dữ liệu duy nhất và không thể thay đổi (immutable) đại diện cho một phần của ứng dụng.
- Action (Hành động): Một thông điệp gửi đi để yêu cầu thay đổi trạng thái. Nó không chứa logic, chỉ chứa dữ liệu cần thiết.
- Reducer (Bộ chuyển đổi): Một hàm thuần túy (pure function) nhận vào State cũ + Action và trả về một State mới.
- Dispatcher (Bộ điều phối): Công cụ dùng để gửi Action vào hệ thống.
State
State trong Blazor là dữ liệu hiện tại của ứng dụng tại một thời điểm cụ thể (ví dụ: giỏ hàng, thông tin đăng nhập, biến
Lưu ý: Không ai được sửa State trực tiếp.
Tại sao State phải "Bất biến" (Immutable)?
Hãy tưởng tượng bạn có một danh sách đơn hàng. Nếu bạn sửa trực tiếp giá tiền của một đơn hàng trong danh sách, UI của Blazor sẽ phải quét qua toàn bộ danh sách để so sánh từng thuộc tính xem cái nào vừa đổi để cập nhật. Việc so sánh "từng li từng tí" này (Deep Comparison) cực kỳ tốn tài nguyên.
Với Immutability:
- Khi cần đổi dữ liệu, bạn tạo một bản sao State mới (một địa chỉ ô nhớ mới).
- Blazor chỉ cần so sánh địa chỉ: OldState == NewState?. Nếu khác địa chỉ, nó biết chắc chắn dữ liệu đã đổi và cần vẽ lại UI.
- Việc so sánh hai địa chỉ ô nhớ (Reference Comparison) diễn ra gần như tức thời.
Nếu có 2 biến (variables) thì có cần 2 State không?
Điều này phụ thuộc vào việc 2 biến đó có cùng mục đích sử dụng (Use Case) hay không.Trường hợp A
Hai biến thuộc cùng một tính năng (1 State)
Nếu bạn có FirstName và LastName. Vì chúng luôn đi cùng nhau trong một "User Profile", bạn nên để chúng trong cùng một State.
public class UserState {
public string FirstName { get; }
public string LastName { get; }
// Constructor...
}
Khi đổi FirstName, bạn tạo một UserState mới chứa FirstName mới và LastName cũ.Trường hợp B
Hai biến độc lập (2 States/Features)
Nếu bạn có biến Counter (đếm số) và biến ThemeColor (màu giao diện). Chúng chẳng liên quan gì đến nhau.
Bạn nên tạo 2 State riêng biệt (trong Fluxor gọi là 2 Feature).
Việc tách nhỏ giúp ứng dụng của bạn không bị "render lại toàn bộ" một cách vô lý. Khi Counter thay đổi, những UI chỉ dùng ThemeColor sẽ không bị ảnh hưởng.
Các cách quản lý State thực tế (Hybrid)
- Fluxor: Quản lý dữ liệu nghiệp vụ chính (Danh sách sản phẩm, Giỏ hàng, Thông tin User).
- State Container: Quản lý trạng thái UI tạm thời (Đóng/mở Sidebar, trạng thái Loading của một vùng cụ thể).
- Cascading Value: Quản lý các cài đặt chung (Ngôn ngữ, Dark/Light Mode).
2. 3 thành phần chính
State (Dữ liệu)
Là dữ liệu hiện tại của app.
public class CounterState
{
public int ClickCount { get; }
private CounterState() {} // Required for creating initial state
public CounterState(int clickCount)
{
ClickCount = clickCount;
}
}
Ghi nhớ: State chỉ đọc, không sửa.
---Action (Yêu cầu)
Là tín hiệu nói rằng: "Tôi muốn làm gì đó".
public class IncrementCounterAction {}
Không chứa logic.
Khi bạn muốn một hành động không chỉ là "gửi tín hiệu" mà còn kèm theo dữ liệu (như số lượng tăng thêm, thông tin người dùng, hoặc nội dung nhập từ form), bạn chỉ cần khai báo thêm các thuộc tính (properties) vào class Action.
Ví dụ: Bạn muốn tăng số lượng nhưng kèm theo tên người thực hiện và ghi chú.
public class IncrementCounterAction
{
public int Amount { get; }
public string UpdatedBy { get; }
public DateTime Timestamp { get; }
public IncrementCounterAction(int amount, string updatedBy)
{
Amount = amount;
UpdatedBy = updatedBy;
Timestamp = DateTime.Now;
}
}
Cập nhật Reducer để xử lý dữ liệu mới
[ReducerMethod]
public static CounterState ReduceIncrementCounterAction(CounterState state, IncrementCounterAction action) =>
new CounterState(
clickCount: state.ClickCount + action.Amount,
lastUpdatedBy: action.UpdatedBy // Giả sử State có field này
);
Dispatch từ UI
// Truyền bao nhiêu tham số tùy thích qua Constructor của Action
Dispatcher.Dispatch(new IncrementCounterAction(10, "Admin"));
---
Reducer (Xử lý)
Nơi duy nhất được phép thay đổi State.
[ReducerMethod]
public static CounterState ReduceIncrementCounterAction(
CounterState state,
IncrementCounterAction action)
{
return new CounterState(state.ClickCount + 1);
}- Tính Read-only: Tốt nhất nên để các thuộc tính trong Action là get; (chỉ đọc) và gán qua Constructor. Điều này đảm bảo dữ liệu không bị thay đổi lung tung trên đường vận chuyển.
- Action không chứa logic: Action chỉ nên chứa dữ liệu thô. Đừng thực hiện tính toán bên trong Action, hãy để việc đó cho Reducer.
- Phân tách rõ ràng: Nếu bạn thấy Action bắt đầu chứa quá nhiều tham số không liên quan, đó là dấu hiệu bạn nên chia nhỏ nó thành nhiều Action khác nhau.
Công thức: State cũ + Action → State mới
3. Cách sử dụng trong App
Bạn cần:
- IState để đọc dữ liệu
- IDispatcher để gửi Action
public class App
{
private readonly IDispatcher Dispatcher;
private readonly IState<CounterState> CounterState;
public App(IDispatcher dispatcher, IState<CounterState> counterState)
{
Dispatcher = dispatcher;
CounterState = counterState;
CounterState.StateChanged += (_, _) =>
{
Console.WriteLine($"Count: {CounterState.Value.ClickCount}");
};
}
public void Run()
{
while (true)
{
var input = Console.ReadLine();
if (input == "1")
Dispatcher.Dispatch(new IncrementCounterAction());
}
}
}
Khi gọi:
Dispatcher.Dispatch(new IncrementCounterAction());
Thì:
- Reducer chạy
- Tạo State mới
- UI tự cập nhật
4. Lưu ý quan trọng
Sai:
state.ClickCount++; // Không nên
Đúng:
return new CounterState(state.ClickCount + 1);
Không viết logic phức tạp trong Reducer (API, database...)
5. Tư duy khi dùng Fluxor
- Không sửa dữ liệu trực tiếp
- Mọi thay đổi đều có “dấu vết”
- Dễ debug hơn
Không còn kiểu: “Ai sửa biến này vậy?”
6. Cấu trúc thư mục
Không nên:
State/
Action/
Reducer/
Nên: (Dạng Folder Structure truyền thống)
/Store
/CounterUseCase
├── CounterState.cs // Định nghĩa State
├── IncrementCounterAction.cs // Định nghĩa Action
├── Reducers.cs // Chứa các hàm xử lý logic (Static)
├── Effects.cs // (Nâng cao) Xử lý gọi API, Side effects
/Pages
├── Counter.razor // UI sử dụng State
/Shared
├── MainLayout.razor
Kết luận
- Không sửa State trực tiếp
- Muốn đổi → Dispatch Action
- Reducer tạo State mới
Nắm được 3 điều này là bạn hiểu Fluxor cơ bản rồi.
Nhận xét
Đăng nhận xét