Quản lý trạng thái Redux với cấu trúc dữ liệu tham chiếu vòng bằng Flatted

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.stringifyFlatted.parse hoặ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ặc Date, 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-persist thông qua việc tùy chỉnh transform để đả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.

Thẻ: redux JavaScript json-parsing state-management flatted

Đăng vào ngày 24 tháng 7 lúc 14:05