import {Meta} from '@storybook/addon-docs/blocks'

<Meta title='@dxtmisha/vi/functional-basic/Classes/DataStorage - Trình Bao bọc Local và Session Storage'/>

# Lớp DataStorage

Một lớp linh hoạt để tương tác với `localStorage` và `sessionStorage` của trình duyệt. Nó bao gồm các tiền tố được tích hợp sẵn, thời gian hết hạn bộ đệm (cache), lưu vào bộ nhớ đệm (memory caching) cho các bản sao hoạt động và chuyển đổi JSON tự động.

> `DataStorage` là một **lớp phụ trợ**, được thiết kế để tương tác cấp thấp với bộ nhớ trình duyệt. Trong hầu hết các trường hợp, bạn nên sử dụng các lớp cấp cao hơn như `Cookie` hoặc `Api` cho các nhu cầu dữ liệu cụ thể.

## Tính năng chính

- **Chuyển đổi Tự động** — tự động xử lý phân tích và chuyển đổi chuỗi JSON cho các cấu hình và đối tượng được lưu trữ.
- **Bộ nhớ đệm Dựa trên Thời gian** — cho phép lưu các đối tượng cùng với mốc thời gian và tôn trọng các ràng buộc về tuổi đời (age) của dữ liệu trong quá trình truy xuất.
- **Quản lý Tiền tố** — hỗ trợ cả tiền tố toàn cục và riêng biệt để cách ly cũng như gom nhóm các khóa trong `localStorage`.
- **Tối ưu Bộ nhớ** — tạo ra các singleton cho những khóa giống nhau nhằm tránh các thao tác đọc đĩa bị lặp lại.
- **Hỗ trợ SSR** — sử dụng cơ chế cách ly theo từng request trong quá trình render phía server để ngăn chặn rò rỉ dữ liệu giữa các phiên làm việc song song.

## Khởi tạo

Để khởi tạo đối tượng, hãy gọi hàm khởi tạo `DataStorage(name, isSession)`.

### Ràng buộc Generic (Generic Constraints)

Lớp hỗ trợ generic sau để đảm bảo kiểm soát kiểu dữ liệu nghiêm ngặt cho giá trị được lưu trữ:
- `T` — Kiểu dữ liệu của giá trị được lưu trữ.

**Tham số:**
- `name: string` — tên của khóa lưu trữ.
- `isSession: boolean = false` — có sử dụng `sessionStorage` hay không (mặc định là `localStorage`).

```typescript
import { DataStorage } from '@dxtmisha/functional-basic'

// 1. Tạo một thực thể cho localStorage
const settings = new DataStorage('settings')

// 2. Tạo một thực thể cho sessionStorage
const session = new DataStorage('user_session', true)
```

## Các phương thức

### Truy cập Bộ lưu trữ

- `get(defaultValue?: T | (() => T), cache?: number): T | undefined` — Truy xuất giá trị hiện được lưu trữ. Nếu thời gian cache được cung cấp, nó sẽ trả về `undefined` nếu dữ liệu đã quá hạn.
- `set(value?: T | (() => T)): T | undefined` — Thiết lập hoặc cập nhật giá trị trong cơ chế lưu trữ. Truyền `undefined` sẽ xóa mục đó.
- `remove(): this` — Xóa khóa tương ứng khỏi môi trường lưu trữ và thực thể trong bộ nhớ.
- `update(): this` — Buộc thực thể bộ nhớ phải đồng bộ hóa với dữ liệu thô hiện tại bên trong các Storage API vật lý.

### Cấu hình

- `static setPrefix(newPrefix: string): void` — Thay đổi tiền tố toàn cục áp dụng cho các thực thể mới. Tiền tố mặc định là `ui-storage`.

## SSR và Hydration

Lớp `DataStorage` hoàn toàn tương thích với Server-Side Rendering (SSR). Nó sử dụng một registry đặc biệt để cách ly các instance giữa các HTTP request khác nhau trên server.

> Để tránh lỗi hydration (khi HTML render từ server khác với client), bạn nên gọi phương thức `get()` bên trong `useEffect` hoặc các hook chỉ chạy ở client. Trên server, `get()` sẽ luôn trả về `undefined` (hoặc `defaultValue`) vì `localStorage` không khả dụng.

## Ví dụ

### Sử dụng Giá trị Mặc định và Cache
```javascript
const settings = new DataStorage('user_settings')

// Lấy dữ liệu, trả về object rỗng nếu không tìm thấy
const currentSettings = settings.get({})

// Chỉ lấy dữ liệu nếu nó được lưu lại trong vòng 60 giây gần nhất
const cachedItem = settings.get(undefined, 60)
```

### Đồng bộ hóa Thủ công
```javascript
const storage = new DataStorage('shared_data')

// ... dữ liệu có thể đã thay đổi trong một tab khác hoặc phần khác của ứng dụng
storage.update() // Đồng bộ hóa bộ nhớ với ổ đĩa
console.log(storage.get())
```
