FEMastery
4Level 4 · Advanced Data & State Management

Data & State Management nâng cao

State đúng và dự đoán được chiếm phần lớn độ tin cậy của frontend — sai ở tầng này, mọi component phía trên đều rung.

Structural sharingImmutable data patternsReferential equalityMemoization pitfallsRace conditions in UI stateFinite state modelingEvent sourcing in frontendOptimistic UI rollback strategyDeterministic renderingIdempotent UI actions
0%
8 phút đọcChia sẻ cấu trúc khi cập nhật

Structural sharing

Cập nhật immutable nhưng chỉ tạo node mới dọc theo đường thay đổi; các nhánh không đổi được tái dùng nguyên vẹn để tối ưu hiệu năng bộ nhớ.

Nguyên lý chia sẻ cấu trúc (Structural Sharing)

Khi làm việc với immutable data, một trong những nỗi lo lớn nhất là hiệu năng: nếu mỗi lần thay đổi một thuộc tính nhỏ, ta lại phải sao chép toàn bộ object khổng lồ thì RAM và CPU sẽ sớm quá tải. Giải pháp cho vấn đề này là Structural Sharing.

Thay vì nhân bản toàn bộ cấu trúc dữ liệu dưới dạng một cây mới hoàn toàn, hệ thống chỉ tạo mới các node nằm trên đường dẫn từ node gốc đến node trực tiếp thay đổi (path to change). Các nhánh (subtree) khác không bị tác động sẽ được giữ nguyên tham chiếu (reference) và liên kết trực tiếp vào node cha mới.

Nhờ vậy, ta có thể so sánh sự thay đổi của cả một nhánh lớn chỉ bằng một phép so sánh bằng tham chiếu === cực kỳ nhanh chóng ở node cha. Nếu tham chiếu trùng khớp, ta biết chắc chắn toàn bộ nhánh đó không có gì thay đổi, cho phép các thư viện như React bỏ qua việc re-render (memoization) một cách an toàn.

ts
const state = {
  user: {
    name: "Hải",
    profile: { age: 25, avatar: "avatar_url" }
  },
  settings: {
    theme: "dark",
    notifications: true
  }
};

// Thực hiện cập nhật avatar của user
const nextState = {
  ...state,
  user: {
    ...state.user,
    profile: {
      ...state.user.profile,
      avatar: "new_avatar_url" // Thay đổi ở đây
    }
  }
  // settings không được nhắc đến -> giữ nguyên tham chiếu từ state cũ
};

console.log(nextState === state); // false (Gốc đã thay đổi)
console.log(nextState.user === state.user); // false (Nằm trên đường dẫn thay đổi)
console.log(nextState.user.profile === state.user.profile); // false (Nằm trên đường dẫn thay đổi)
console.log(nextState.settings === state.settings); // true (Chia sẻ cấu trúc thành công!)
Chỉ clone dọc theo đường dẫn thay đổi, giữ nguyên reference các nhánh khác

VÌ SAOGiúp so sánh cực nhanh các nhánh dữ liệu lớn bằng phép so sánh tham chiếu === thay vì phải duyệt sâu (deep comparison) tốn kém.

MẸONếu dùng React, việc giữ nguyên tham chiếu cho các nhánh không đổi giúp React.memo hoạt động hoàn hảo, chặn đứng các lượt render thừa.

Lược đồ hóa bộ nhớ và các công cụ hỗ trợ

Hãy hình dung cấu trúc dữ liệu như một cái cây. Khi bạn đổi một lá ở bên trái, bạn không cần trồng lại nửa cái cây bên phải. Bạn chỉ cần tạo ra các cành mới dẫn từ gốc đến chiếc lá đó. Các cành bên phải vẫn được nối vào gốc mới mà không cần nhân bản.

Trong Javascript, việc này được thực hiện thông qua toán tử spread { ... } hoặc các cấu trúc dữ liệu chuyên biệt (như trong Immutable.js hoặc Immer). Immer sử dụng cơ chế Proxy để tự động theo dõi các thuộc tính bạn chỉnh sửa và thực hiện chia sẻ cấu trúc dưới mui xe (under the hood) một cách tối ưu nhất.

NÊNHãy sử dụng Immer hoặc Redux Toolkit khi cập nhật các nested state phức tạp để tránh việc viết quá nhiều toán tử spread lồng nhau dễ dẫn đến sai sót.