Trong phát triển ứng dụng React/Redux, việc tuần tự hóa (serialization) trạng thái để lưu trữ vào LocalStorage hoặc gửi qua mạng là một thao tác phổ biến. Tuy nhiên, phương thức JSON.stringify mặc định của JavaScript sẽ ném ra lỗi nếu gặp phải cấu trúc dữ liệu có tham chiếu vòng (circular reference). Đây là lúc Flatted trở thành một giải pháp thay thế mạnh mẽ và gọn nhẹ.
Thách thức của tham chiếu vòng trong Redux
Tham chiếu vòng thường xuất hiện trong các mô hình dữ liệu phức tạp, chẳng hạn như cấu trúc đồ thị (Graph) hoặc các quan hệ thực thể liên kết ngược (ví dụ: User A chứa User B, và User B cũng chứa User A trong danh sách bạn bè). Khi Redux cố gắng xử lý các đối tượng này để lưu trữ trạng thái, ứng dụng sẽ bị crash ngay lập tức.
Flatted giải quyết vấn đề này bằng cách sử dụng cơ chế lập chỉ mục (indexing), biến các tham chiếu lặp lại thành các định danh duy nhất, từ đó cho phép tuần tự hóa và giải mã mà không làm mất đi cấu trúc gốc của dữ liệu.
Tích hợp Flatted vào quy trình làm việc của Redux
1. Xây dựng Middleware để lưu trữ trạng thái
Thay vì sử dụng bộ tuần tự hóa mặc định, chúng ta có thể viết một middleware tùy chỉnh để tự động lưu trạng thái vào localStorage mỗi khi có action được thực thi.
import { stringify } from 'flatted';
const persistenceMiddleware = store => next => action => {
const nextAction = next(action);
const currentState = store.getState();
try {
const serializedData = stringify(currentState);
localStorage.setItem('APP_PERSISTENT_STATE', serializedData);
} catch (err) {
console.error('Không thể lưu trữ trạng thái:', err);
}
return nextAction;
};
2. Khôi phục trạng thái (Rehydration)
Khi ứng dụng khởi tạo, chúng ta cần giải mã dữ liệu từ bộ nhớ để đưa vào initialState của Redux store.
import { parse } from 'flatted';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
const loadState = () => {
try {
const rawData = localStorage.getItem('APP_PERSISTENT_STATE');
if (rawData === null) return undefined;
return parse(rawData);
} catch (err) {
return undefined;
}
};
const store = createStore(
rootReducer,
loadState(),
applyMiddleware(persistenceMiddleware)
);
Ứng dụng thực tế: Quản lý mạng lưới quan hệ
Giả sử bạn đang xây dựng một ứng dụng quản lý dự án, nơi Project có danh sách Member, và mỗi Member lại giữ tham chiếu đến Project mà họ đang tham gia. Đây là kịch bản điển hình gây lỗi cho JSON.stringify.
const member = { id: 1, name: 'Dev A' };
const project = { id: 101, title: 'Hệ thống ERP' };
// Tạo tham chiếu vòng
member.assignedProject = project;
project.leadDeveloper = member;
// Với Flatted, việc lưu trữ này hoàn toàn hợp lệ
const dataStr = stringify(project);
const restoredProject = parse(dataStr);
console.log(restoredProject.leadDeveloper.assignedProject.id); // Kết quả: 101
So sánh đặc tính kỹ thuật
| Tiêu chí | JSON Standard | Flatted |
|---|---|---|
| Xử lý tham chiếu vòng | Gây lỗi TypeError | Hỗ trợ đầy đủ |
| Kích thước thư viện | Có sẵn trong Runtime | Siêu nhỏ (~0.5KB) |
| Hiệu suất | Nhanh nhất | Tối ưu hóa cao |
| Khả năng tương thích API | Chuẩn hóa | Tương đồng với JSON.parse/stringify |
Lưu ý khi triển khai
- Đồng bộ hóa phương thức: Tuyệt đối không trộn lẫn giữa
JSON.stringifyvàFlatted.parsehoặc ngược lại. Cấu trúc chuỗi mà Flatted tạo ra khác hoàn toàn với JSON chuẩn. - Kiểu dữ liệu nâng cao: Flatted tập trung vào việc giải quyết tham chiếu vòng cho các Object và Array. Nếu trạng thái của bạn chứa
Map,Set, hoặcDate, bạn cần xử lý chuyển đổi chúng về Object/Array trước khi tuần tự hóa hoặc sử dụng các trình thay thế (replacer/reviver) đi kèm. - Sử dụng trong Redux Toolkit: Nếu bạn dùng Redux Toolkit, hãy tích hợp Flatted vào cấu hình của
redux-persistthông qua việc tùy chỉnhtransformđể đảm bảo dữ liệu được xử lý đúng cách trước khi ghi vào storage.
Việc áp dụng Flatted giúp kiến trúc Redux của bạn trở nên bền bỉ hơn, cho phép mô hình hóa dữ liệu theo cách tự nhiên nhất mà không bị giới hạn bởi những rào cản kỹ thuật của định dạng JSON truyền thống.