# react-native-hox

面向 React Native 的**极简状态管理**：类型安全、API 零心智负担，单文件架构，包体积极小。

[![NPM Version](https://img.shields.io/npm/v/react-native-hox)](https://www.npmjs.com/package/react-native-hox)
[![TypeScript](https://img.shields.io/badge/language-TypeScript-blue)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/npm/l/react-native-hox)](https://www.npmjs.com/package/react-native-hox)

---

## 亮点

| 特性 | 说明 |
|------|------|
| **包体积极小** | 单入口构建，**gzip 后 ESM ~1.5 KB / CJS ~1.6 KB**；运行时仅依赖 `use-sync-external-store`，持久化可选依赖 AsyncStorage |
| **类型安全** | 全 TypeScript，`createModel` 泛型推导完整，selector 与 setState 类型即文档 |
| **API 极简** | 一个 `createModel` 搞定：组件内用 Hook，组件外用 `model.data`，无需 Provider |
| **按需重渲染** | 支持 selector + 可选 `shallow`，只在关注字段变化时更新组件 |
| **可选持久化** | 内置 persist（默认 AsyncStorage），支持自定义 storage、防抖、迁移钩子 |
| **React 18 友好** | 基于 `useSyncExternalStore`，无 tearing，兼容并发渲染 |

---

## 快速开始

### 安装

```bash
npm i react-native-hox @react-native-async-storage/async-storage
```

> 项目中已有 `@react-native-async-storage/async-storage` 则无需重复安装。

### 最小示例

```ts
// stores/user.ts
import { createModel } from 'react-native-hox';

export const userStore = createModel({ name: 'Guest' });
// 持久化：createModel({ name: 'Guest' }, { persist: 'user_v1' });
```

```tsx
// Profile.tsx
import React from 'react';
import { View, Text, Button } from 'react-native';
import { userStore } from './stores/user';

export function Profile() {
  const user = userStore.getState();
  return (
    <View>
      <Text>Hello, {user.name}</Text>
      <Button title="Set Name" onPress={() => userStore.setState({ name: 'Tom' })} />
    </View>
  );
}
```

---

## 功能概览

### 1) 更新状态：自动合并 / 显式替换

```ts
const profileStore = createModel({ name: 'Guest', role: 'user' as 'user' | 'admin' });
```

```tsx
profileStore.setState({ name: 'Alice' });                    // 合并
profileStore.setState({ name: 'Bob', role: 'admin' }, true);  // 整体替换
```

### 2) Selector：只在关注数据变化时重渲染

```tsx
const count = counterStore.getState((s) => s.count);
// 返回对象时建议用 shallow
const picked = counterStore.getState((s) => ({ count: s.count }), shallow);
```

### 3) 组件外读写：Vanilla API

```ts
const name = userStore.data.state.name;
userStore.data.getState();
userStore.data.subscribe((next, prev) => {});
```

### 4) 持久化

```ts
createModel({ token: '' }, { persist: 'auth_v1' });
// 或自定义 storage / beforeRecover / beforePersist
createModel({ token: '' }, { persist: { key: 'auth_v1', storage: myStorage } });
```

### 5) reset / destroy / strict

```ts
userStore.reset();
userStore.destroy();
createModel({ count: 0 }, { strict: { forbidSetStateAfterDestroy: true } });
```

---

## 进阶

### subscribe(selector)：按 slice 订阅

```tsx
userStore.data.subscribe(
  (s) => s.name,
  (next, prev) => console.log(next, prev),
  { fireImmediately: true }
);
```

### 持久化迁移

```ts
createModel(
  { token: '', isLogin: false },
  {
    persist: {
      key: 'user_v1',
      beforeRecover: (v) => ({ token: v?.token ?? '', isLogin: Boolean(v?.token) }),
    },
  }
);
```

### 初始状态用函数

```ts
createModel(() => ({ bootAt: Date.now() }));
```

---

## API 简表

| 方法 / 属性 | 说明 |
|-------------|------|
| `model()` / `model.getState()` / `model.use()` / `model.useState()` | 组件内 Hook，可传 selector + equalityFn |
| `model.setState(partial, replace?)` | 更新状态 |
| `model.data.state` / `model.data.getState()` | 组件外读状态 |
| `model.data.setState` / `model.data.subscribe` | 组件外写与订阅 |
| `model.reset()` / `model.destroy()` | 重置为初始值、销毁 |

**options**：`persist`（string 或 `{ key, storage?, debounce?, beforePersist?, beforeRecover? }`）、`logger`、`strict.forbidSetStateAfterDestroy`。

---

## License

ISC
