react-native-hox 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +160 -133
- package/dist/index.d.mts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +17 -35
- package/dist/index.mjs +16 -35
- package/package.json +5 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 wangws
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,168 +1,204 @@
|
|
|
1
1
|
# react-native-hox
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> 极简、高性能、生产级特性开箱即用。
|
|
3
|
+
面向 React Native 的轻量状态管理方案:类型安全、API 极简,支持组件内 Hook 与组件外 Vanilla 读写,并内置可选持久化能力。
|
|
5
4
|
|
|
6
5
|
[](https://www.npmjs.com/package/react-native-hox)
|
|
7
6
|
[](https://www.typescriptlang.org/)
|
|
8
|
-
[](https://
|
|
7
|
+
[](https://www.npmjs.com/package/react-native-hox)
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## 快速开始
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
### 安装
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
```bash
|
|
14
|
+
npm i react-native-hox
|
|
15
|
+
```
|
|
15
16
|
|
|
16
|
-
###
|
|
17
|
+
### 最小示例
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* 🎯 **精准渲染**:支持 Selector 选择器,确保组件仅在关注的数据变化时更新。
|
|
22
|
-
* 🔌 **脱离组件运行**:完整的 Vanilla API,支持在 Axios 拦截器、事件回调等非组件环境读写状态。
|
|
19
|
+
```ts
|
|
20
|
+
// stores/user.ts
|
|
21
|
+
import { createModel } from 'react-native-hox';
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
export const userStore = createModel({ name: 'Guest' }); // 数据只存在内存
|
|
24
|
+
// export const userStore = createModel({ name: 'Guest' }, { persist: 'user_v1' }); // 数据存在缓存 AsyncStorage
|
|
25
|
+
```
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
```tsx
|
|
28
|
+
// Profile.tsx
|
|
29
|
+
import React from 'react';
|
|
30
|
+
import { View, Text, Button } from 'react-native';
|
|
31
|
+
import { userStore } from './stores/user';
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
export function Profile() {
|
|
34
|
+
const user = userStore.getState();
|
|
35
|
+
return (
|
|
36
|
+
<View>
|
|
37
|
+
<Text>Hello, {user.name}</Text>
|
|
38
|
+
<Button title="Set Name" onPress={() => userStore.setState({ name: 'Tom' })} />
|
|
39
|
+
</View>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
```
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
## 功能点
|
|
45
|
+
|
|
46
|
+
### 1) 更新状态:自动合并 / 显式替换
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { createModel } from 'react-native-hox';
|
|
50
|
+
|
|
51
|
+
export const profileStore = createModel({ name: 'Guest', role: 'user' as 'user' | 'admin' });
|
|
34
52
|
```
|
|
35
53
|
|
|
36
|
-
|
|
54
|
+
```tsx
|
|
55
|
+
import React from 'react';
|
|
56
|
+
import { View, Text, Button } from 'react-native';
|
|
57
|
+
import { profileStore } from './stores/profile';
|
|
37
58
|
|
|
38
|
-
|
|
59
|
+
export function UpdateExample() {
|
|
60
|
+
const profile = profileStore.getState();
|
|
39
61
|
|
|
40
|
-
|
|
41
|
-
|
|
62
|
+
return (
|
|
63
|
+
<View>
|
|
64
|
+
<Text>{profile.name}</Text>
|
|
65
|
+
<Text>{profile.role}</Text>
|
|
66
|
+
<Button title="Merge Update" onPress={() => profileStore.setState({ name: 'Alice' })} />
|
|
67
|
+
<Button title="Replace" onPress={() => profileStore.setState({ name: 'Bob', role: 'admin' }, true)} />
|
|
68
|
+
</View>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2) Selector:只在关注的数据变化时重渲染
|
|
74
|
+
|
|
75
|
+
```ts
|
|
42
76
|
import { createModel } from 'react-native-hox';
|
|
43
77
|
|
|
44
|
-
|
|
45
|
-
export const userStore = createModel({
|
|
46
|
-
name: 'Guest',
|
|
47
|
-
token: '',
|
|
48
|
-
isLogin: false,
|
|
49
|
-
}, {
|
|
50
|
-
persist: 'user_v1', // 开启持久化,数据自动存入 AsyncStorage/localStorage
|
|
51
|
-
});
|
|
78
|
+
export const counterStore = createModel({ count: 0, text: 'a' });
|
|
52
79
|
```
|
|
53
80
|
|
|
54
81
|
```tsx
|
|
55
|
-
|
|
56
|
-
import {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const name = userStore.getState(s => s.name);
|
|
62
|
-
|
|
63
|
-
const login = () => {
|
|
64
|
-
// 2. 更新状态:自动合并,无需 ...user
|
|
65
|
-
userStore.setState({
|
|
66
|
-
name: 'Admin',
|
|
67
|
-
isLogin: true
|
|
68
|
-
});
|
|
69
|
-
};
|
|
82
|
+
import React from 'react';
|
|
83
|
+
import { View, Text, Button } from 'react-native';
|
|
84
|
+
import { counterStore } from './stores/counter';
|
|
85
|
+
|
|
86
|
+
export function SelectorExample() {
|
|
87
|
+
const count = counterStore.getState((s) => s.count);
|
|
70
88
|
|
|
71
89
|
return (
|
|
72
90
|
<View>
|
|
73
|
-
<Text>
|
|
74
|
-
<Button onPress={
|
|
91
|
+
<Text>{count}</Text>
|
|
92
|
+
<Button title="Inc" onPress={() => counterStore.setState((s) => ({ count: s.count + 1 }))} />
|
|
93
|
+
<Button title="Change Text" onPress={() => counterStore.setState({ text: String(Math.random()) })} />
|
|
75
94
|
</View>
|
|
76
95
|
);
|
|
77
96
|
}
|
|
78
97
|
```
|
|
79
98
|
|
|
80
|
-
|
|
99
|
+
当 selector 返回对象/数组时,传入 `equalityFn`(例如 `shallow`)避免无意义重渲染:
|
|
81
100
|
|
|
82
101
|
```tsx
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```
|
|
102
|
+
import { shallow } from 'react-native-hox';
|
|
103
|
+
import { counterStore } from './stores/counter';
|
|
104
|
+
|
|
105
|
+
const picked = counterStore.getState((s) => ({ count: s.count }), shallow);
|
|
106
|
+
```
|
|
89
107
|
|
|
90
|
-
|
|
108
|
+
### 3) 组件外读写:Vanilla API
|
|
91
109
|
|
|
92
|
-
|
|
110
|
+
```ts
|
|
111
|
+
import { userStore } from './stores/user';
|
|
93
112
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
**现在**:
|
|
113
|
+
const name1 = userStore.data.state.name;
|
|
114
|
+
const name2 = userStore.data.getState().name;
|
|
97
115
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
persist: 'app_theme' // ✅ Done. 自动防抖,自动恢复。
|
|
116
|
+
const unsub = userStore.data.subscribe((next, prev) => {
|
|
117
|
+
console.log(next, prev);
|
|
101
118
|
});
|
|
119
|
+
unsub();
|
|
102
120
|
```
|
|
103
121
|
|
|
104
|
-
###
|
|
105
|
-
**以前**:为了在 API 拦截器里拿 Token,不得不把 store 挂载到 window 或者引入复杂的 hack。
|
|
106
|
-
**现在**:
|
|
122
|
+
### 4) 持久化:persist(默认 AsyncStorage)
|
|
107
123
|
|
|
108
|
-
|
|
109
|
-
// api.ts
|
|
110
|
-
import { userStore } from './store/user';
|
|
124
|
+
只要提供一个 key 即可开启持久化(默认使用 `@react-native-async-storage/async-storage`,无需重复传 `storage`):
|
|
111
125
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
return config;
|
|
119
|
-
});
|
|
126
|
+
```ts
|
|
127
|
+
import { createModel } from 'react-native-hox';
|
|
128
|
+
|
|
129
|
+
export const authStore = createModel({ token: '' }, { persist: 'auth_v1' });
|
|
130
|
+
```
|
|
120
131
|
|
|
121
|
-
|
|
122
|
-
userStore.data.setState({ token: '' }); // 登出
|
|
123
|
-
```+ ### 3. API 统一性 (New)
|
|
124
|
-
+ **以前**:Redux `useSelector` vs `dispatch`,Context `useContext` vs `dispatch`。
|
|
125
|
-
+ **现在**:
|
|
126
|
-
+ * 读 (React):`userStore.getState()`
|
|
127
|
-
+ * 读 (Vanilla):`userStore.data.state`
|
|
128
|
-
+ * 写 (Universal):`userStore.setState()`
|
|
132
|
+
如果你希望替换存储引擎,可以在 `persist.storage` 传入实现同等接口的存储对象:
|
|
129
133
|
|
|
130
|
-
|
|
134
|
+
```ts
|
|
135
|
+
import { createModel } from 'react-native-hox';
|
|
136
|
+
import type { StorageEngine } from 'react-native-hox';
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
const storage: StorageEngine = {
|
|
139
|
+
getItem: async (key) => null,
|
|
140
|
+
setItem: async (key, value) => {},
|
|
141
|
+
removeItem: async (key) => {},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const authStore = createModel({ token: '' }, { persist: { key: 'auth_v1', storage } });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 5) reset / destroy / strict
|
|
133
148
|
|
|
134
149
|
```ts
|
|
135
|
-
import
|
|
136
|
-
import { userStore } from './store/user';
|
|
137
|
-
|
|
138
|
-
axios.interceptors.request.use((config) => {
|
|
139
|
-
const token = userStore.data.state.token;
|
|
140
|
-
if (token) {
|
|
141
|
-
config.headers = config.headers ?? {};
|
|
142
|
-
config.headers.Authorization = `Bearer ${token}`;
|
|
143
|
-
}
|
|
144
|
-
return config;
|
|
145
|
-
});
|
|
150
|
+
import { userStore } from './stores/user';
|
|
146
151
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
userStore.reset();
|
|
153
|
+
userStore.destroy();
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { createModel } from 'react-native-hox';
|
|
158
|
+
|
|
159
|
+
export const strictStore = createModel(
|
|
160
|
+
{ count: 0 },
|
|
161
|
+
{ strict: { forbidSetStateAfterDestroy: true } }
|
|
155
162
|
);
|
|
156
163
|
```
|
|
157
164
|
|
|
158
|
-
|
|
165
|
+
## 进阶使用
|
|
159
166
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
### 1) subscribe(selector):只在 slice 变化时触发
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import React, { useEffect } from 'react';
|
|
171
|
+
import { View, Text } from 'react-native';
|
|
172
|
+
import { userStore } from './stores/user';
|
|
173
|
+
|
|
174
|
+
export function SubscribeExample() {
|
|
175
|
+
const name = userStore.getState((s) => s.name);
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
const unsub = userStore.data.subscribe(
|
|
179
|
+
(s) => s.name,
|
|
180
|
+
(next, prev) => {
|
|
181
|
+
console.log('name changed:', prev, '->', next);
|
|
182
|
+
},
|
|
183
|
+
{ fireImmediately: true }
|
|
184
|
+
);
|
|
185
|
+
return unsub;
|
|
186
|
+
}, []);
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<View>
|
|
190
|
+
<Text>{name}</Text>
|
|
191
|
+
</View>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 2) 持久化安全边界:版本化与数据迁移
|
|
163
197
|
|
|
164
198
|
```ts
|
|
165
|
-
|
|
199
|
+
import { createModel } from 'react-native-hox';
|
|
200
|
+
|
|
201
|
+
export const userPersistStore = createModel(
|
|
166
202
|
{ token: '', isLogin: false },
|
|
167
203
|
{
|
|
168
204
|
persist: {
|
|
@@ -176,37 +212,28 @@ export const userStore = createModel(
|
|
|
176
212
|
);
|
|
177
213
|
```
|
|
178
214
|
|
|
179
|
-
### 3)
|
|
215
|
+
### 3) initialState 支持纯函数
|
|
180
216
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
```tsx
|
|
185
|
-
import { shallow } from 'react-native-hox';
|
|
217
|
+
```ts
|
|
218
|
+
import { createModel } from 'react-native-hox';
|
|
186
219
|
|
|
187
|
-
const
|
|
220
|
+
export const appStore = createModel(() => ({ bootAt: Date.now() }));
|
|
188
221
|
```
|
|
189
222
|
|
|
190
|
-
## API
|
|
223
|
+
## API 参考(简版)
|
|
191
224
|
|
|
192
225
|
### `createModel(initialState, options?)`
|
|
193
226
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
* `persist`: `string` (可选) - 持久化存储的唯一 Key。
|
|
227
|
+
- `initialState`: `T` 或 `() => T`
|
|
228
|
+
- `options.persist`: `string` 或 `{ key, storage?, debounce?, beforePersist?, beforeRecover? }`
|
|
229
|
+
- `options.strict.forbidSetStateAfterDestroy`: `boolean`
|
|
230
|
+
- `options.logger`: `{ warn, error }`
|
|
199
231
|
|
|
200
|
-
|
|
201
|
-
* `getState(selector?, equalityFn?)`: React Hook,在组件中读取状态。
|
|
202
|
-
* `useState(selector?, equalityFn?)`: `getState` 的别名。
|
|
203
|
-
* `use(selector?, equalityFn?)`: `getState` 的别名。
|
|
204
|
-
* `setState(patch)`: 更新状态。
|
|
205
|
-
* `reset()`: 重置为初始状态。
|
|
206
|
-
* `data`: 组件外 API,包含 `state` (getter), `getState` (fn), `setState`, `subscribe`, `reset`, `destroy`。
|
|
232
|
+
返回的 `Model` 同时支持:
|
|
207
233
|
|
|
208
|
-
|
|
234
|
+
- 组件内(Hook):`model.getState()` / `model.use()` / `model.useState()`
|
|
235
|
+
- 组件外(Vanilla):`model.data.state` / `model.data.getState()` / `model.data.setState()` / `model.data.subscribe()`
|
|
209
236
|
|
|
210
237
|
## License
|
|
211
238
|
|
|
212
|
-
|
|
239
|
+
ISC
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
type Listener<T> = (state: T, prevState: T) => void;
|
|
1
2
|
interface StorageEngine {
|
|
2
3
|
getItem: (key: string) => string | null | Promise<string | null>;
|
|
3
4
|
setItem: (key: string, value: string) => void | Promise<void>;
|
|
4
5
|
removeItem: (key: string) => void | Promise<void>;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
type Listener<T> = (state: T, prevState: T) => void;
|
|
8
7
|
type EqualityFn<T> = (a: T, b: T) => boolean;
|
|
9
8
|
|
|
10
9
|
type Selector<T, U> = (state: T) => U;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
type Listener<T> = (state: T, prevState: T) => void;
|
|
1
2
|
interface StorageEngine {
|
|
2
3
|
getItem: (key: string) => string | null | Promise<string | null>;
|
|
3
4
|
setItem: (key: string, value: string) => void | Promise<void>;
|
|
4
5
|
removeItem: (key: string) => void | Promise<void>;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
type Listener<T> = (state: T, prevState: T) => void;
|
|
8
7
|
type EqualityFn<T> = (a: T, b: T) => boolean;
|
|
9
8
|
|
|
10
9
|
type Selector<T, U> = (state: T) => U;
|
package/dist/index.js
CHANGED
|
@@ -1,43 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var useSyncExternalStoreExports = require('use-sync-external-store/shim/with-selector');
|
|
4
|
+
var AsyncStorage = require('@react-native-async-storage/async-storage');
|
|
4
5
|
|
|
5
6
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
7
|
|
|
7
8
|
var useSyncExternalStoreExports__default = /*#__PURE__*/_interopDefault(useSyncExternalStoreExports);
|
|
9
|
+
var AsyncStorage__default = /*#__PURE__*/_interopDefault(AsyncStorage);
|
|
8
10
|
|
|
9
11
|
// src/createModel.ts
|
|
10
12
|
|
|
11
|
-
// src/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (hasCachedDefaultWebStorage) {
|
|
16
|
-
return cachedDefaultWebStorage;
|
|
17
|
-
}
|
|
18
|
-
hasCachedDefaultWebStorage = true;
|
|
19
|
-
try {
|
|
20
|
-
const anyGlobal = globalThis;
|
|
21
|
-
const storage = anyGlobal == null ? void 0 : anyGlobal.localStorage;
|
|
22
|
-
if (!storage) {
|
|
23
|
-
return void 0;
|
|
24
|
-
}
|
|
25
|
-
const testKey = "__react_native_hox_test__";
|
|
26
|
-
storage.setItem(testKey, "1");
|
|
27
|
-
storage.removeItem(testKey);
|
|
28
|
-
cachedDefaultWebStorage = {
|
|
29
|
-
getItem: (key) => storage.getItem(key),
|
|
30
|
-
setItem: (key, value) => storage.setItem(key, value),
|
|
31
|
-
removeItem: (key) => storage.removeItem(key)
|
|
32
|
-
};
|
|
33
|
-
return cachedDefaultWebStorage;
|
|
34
|
-
} catch {
|
|
35
|
-
cachedDefaultWebStorage = void 0;
|
|
36
|
-
return void 0;
|
|
13
|
+
// src/vanilla.ts
|
|
14
|
+
function isPlainObject(value) {
|
|
15
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") {
|
|
16
|
+
return false;
|
|
37
17
|
}
|
|
18
|
+
const proto = Object.getPrototypeOf(value);
|
|
19
|
+
return proto === Object.prototype || proto === null;
|
|
38
20
|
}
|
|
39
|
-
|
|
40
|
-
// src/vanilla.ts
|
|
41
21
|
var createStore = (initialState) => {
|
|
42
22
|
let state = initialState;
|
|
43
23
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -45,8 +25,10 @@ var createStore = (initialState) => {
|
|
|
45
25
|
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
46
26
|
if (!Object.is(nextState, state)) {
|
|
47
27
|
const previousState = state;
|
|
48
|
-
|
|
49
|
-
|
|
28
|
+
const shouldReplace = replace != null ? replace : !isPlainObject(state) || !isPlainObject(nextState);
|
|
29
|
+
state = shouldReplace ? nextState : Object.assign({}, state, nextState);
|
|
30
|
+
const snapshot = new Set(listeners);
|
|
31
|
+
snapshot.forEach((listener) => listener(state, previousState));
|
|
50
32
|
}
|
|
51
33
|
};
|
|
52
34
|
const getState = () => state;
|
|
@@ -137,7 +119,7 @@ function createModel(initialState, options) {
|
|
|
137
119
|
isDestroyed = true;
|
|
138
120
|
store.destroy();
|
|
139
121
|
};
|
|
140
|
-
const reset = () =>
|
|
122
|
+
const reset = () => setState(initialValue, true);
|
|
141
123
|
const subscribe = createSubscribe(store);
|
|
142
124
|
const data = {
|
|
143
125
|
getState: store.getState,
|
|
@@ -147,7 +129,8 @@ function createModel(initialState, options) {
|
|
|
147
129
|
reset
|
|
148
130
|
};
|
|
149
131
|
Object.defineProperty(data, "state", {
|
|
150
|
-
get: () => store.getState()
|
|
132
|
+
get: () => store.getState(),
|
|
133
|
+
enumerable: true
|
|
151
134
|
});
|
|
152
135
|
const hook = createModelHook(store);
|
|
153
136
|
const model = {
|
|
@@ -216,7 +199,7 @@ function setupModelSideEffects(store, options, logger) {
|
|
|
216
199
|
var _a, _b, _c, _d;
|
|
217
200
|
const persist = typeof (options == null ? void 0 : options.persist) === "string" ? { key: options.persist } : options == null ? void 0 : options.persist;
|
|
218
201
|
const persistKey = persist == null ? void 0 : persist.key;
|
|
219
|
-
const storage = (_a = persist == null ? void 0 : persist.storage) != null ? _a :
|
|
202
|
+
const storage = (_a = persist == null ? void 0 : persist.storage) != null ? _a : AsyncStorage__default.default;
|
|
220
203
|
if (!persistKey) {
|
|
221
204
|
return;
|
|
222
205
|
}
|
|
@@ -225,7 +208,7 @@ function setupModelSideEffects(store, options, logger) {
|
|
|
225
208
|
const debounceMs = (_d = persist == null ? void 0 : persist.debounce) != null ? _d : 100;
|
|
226
209
|
if (!storage) {
|
|
227
210
|
logger.warn(
|
|
228
|
-
"[react-native-hox] persist \u5DF2\u5F00\u542F\u4F46 storage \u4E0D\u53EF\u7528\
|
|
211
|
+
"[react-native-hox] persist \u5DF2\u5F00\u542F\u4F46 storage \u4E0D\u53EF\u7528\uFF1A\u8BF7\u5728 persist.storage \u4F20\u5165 AsyncStorage/MMKV \u7B49\u3002"
|
|
229
212
|
);
|
|
230
213
|
return;
|
|
231
214
|
}
|
|
@@ -263,7 +246,6 @@ function setupModelSideEffects(store, options, logger) {
|
|
|
263
246
|
unsubscribe();
|
|
264
247
|
save.flush();
|
|
265
248
|
isDestroyed = true;
|
|
266
|
-
save.cancel();
|
|
267
249
|
originalDestroy();
|
|
268
250
|
};
|
|
269
251
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,37 +1,16 @@
|
|
|
1
1
|
import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector';
|
|
2
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
3
|
|
|
3
4
|
// src/createModel.ts
|
|
4
5
|
|
|
5
|
-
// src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (hasCachedDefaultWebStorage) {
|
|
10
|
-
return cachedDefaultWebStorage;
|
|
11
|
-
}
|
|
12
|
-
hasCachedDefaultWebStorage = true;
|
|
13
|
-
try {
|
|
14
|
-
const anyGlobal = globalThis;
|
|
15
|
-
const storage = anyGlobal == null ? void 0 : anyGlobal.localStorage;
|
|
16
|
-
if (!storage) {
|
|
17
|
-
return void 0;
|
|
18
|
-
}
|
|
19
|
-
const testKey = "__react_native_hox_test__";
|
|
20
|
-
storage.setItem(testKey, "1");
|
|
21
|
-
storage.removeItem(testKey);
|
|
22
|
-
cachedDefaultWebStorage = {
|
|
23
|
-
getItem: (key) => storage.getItem(key),
|
|
24
|
-
setItem: (key, value) => storage.setItem(key, value),
|
|
25
|
-
removeItem: (key) => storage.removeItem(key)
|
|
26
|
-
};
|
|
27
|
-
return cachedDefaultWebStorage;
|
|
28
|
-
} catch {
|
|
29
|
-
cachedDefaultWebStorage = void 0;
|
|
30
|
-
return void 0;
|
|
6
|
+
// src/vanilla.ts
|
|
7
|
+
function isPlainObject(value) {
|
|
8
|
+
if (Object.prototype.toString.call(value) !== "[object Object]") {
|
|
9
|
+
return false;
|
|
31
10
|
}
|
|
11
|
+
const proto = Object.getPrototypeOf(value);
|
|
12
|
+
return proto === Object.prototype || proto === null;
|
|
32
13
|
}
|
|
33
|
-
|
|
34
|
-
// src/vanilla.ts
|
|
35
14
|
var createStore = (initialState) => {
|
|
36
15
|
let state = initialState;
|
|
37
16
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -39,8 +18,10 @@ var createStore = (initialState) => {
|
|
|
39
18
|
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
40
19
|
if (!Object.is(nextState, state)) {
|
|
41
20
|
const previousState = state;
|
|
42
|
-
|
|
43
|
-
|
|
21
|
+
const shouldReplace = replace != null ? replace : !isPlainObject(state) || !isPlainObject(nextState);
|
|
22
|
+
state = shouldReplace ? nextState : Object.assign({}, state, nextState);
|
|
23
|
+
const snapshot = new Set(listeners);
|
|
24
|
+
snapshot.forEach((listener) => listener(state, previousState));
|
|
44
25
|
}
|
|
45
26
|
};
|
|
46
27
|
const getState = () => state;
|
|
@@ -131,7 +112,7 @@ function createModel(initialState, options) {
|
|
|
131
112
|
isDestroyed = true;
|
|
132
113
|
store.destroy();
|
|
133
114
|
};
|
|
134
|
-
const reset = () =>
|
|
115
|
+
const reset = () => setState(initialValue, true);
|
|
135
116
|
const subscribe = createSubscribe(store);
|
|
136
117
|
const data = {
|
|
137
118
|
getState: store.getState,
|
|
@@ -141,7 +122,8 @@ function createModel(initialState, options) {
|
|
|
141
122
|
reset
|
|
142
123
|
};
|
|
143
124
|
Object.defineProperty(data, "state", {
|
|
144
|
-
get: () => store.getState()
|
|
125
|
+
get: () => store.getState(),
|
|
126
|
+
enumerable: true
|
|
145
127
|
});
|
|
146
128
|
const hook = createModelHook(store);
|
|
147
129
|
const model = {
|
|
@@ -210,7 +192,7 @@ function setupModelSideEffects(store, options, logger) {
|
|
|
210
192
|
var _a, _b, _c, _d;
|
|
211
193
|
const persist = typeof (options == null ? void 0 : options.persist) === "string" ? { key: options.persist } : options == null ? void 0 : options.persist;
|
|
212
194
|
const persistKey = persist == null ? void 0 : persist.key;
|
|
213
|
-
const storage = (_a = persist == null ? void 0 : persist.storage) != null ? _a :
|
|
195
|
+
const storage = (_a = persist == null ? void 0 : persist.storage) != null ? _a : AsyncStorage;
|
|
214
196
|
if (!persistKey) {
|
|
215
197
|
return;
|
|
216
198
|
}
|
|
@@ -219,7 +201,7 @@ function setupModelSideEffects(store, options, logger) {
|
|
|
219
201
|
const debounceMs = (_d = persist == null ? void 0 : persist.debounce) != null ? _d : 100;
|
|
220
202
|
if (!storage) {
|
|
221
203
|
logger.warn(
|
|
222
|
-
"[react-native-hox] persist \u5DF2\u5F00\u542F\u4F46 storage \u4E0D\u53EF\u7528\
|
|
204
|
+
"[react-native-hox] persist \u5DF2\u5F00\u542F\u4F46 storage \u4E0D\u53EF\u7528\uFF1A\u8BF7\u5728 persist.storage \u4F20\u5165 AsyncStorage/MMKV \u7B49\u3002"
|
|
223
205
|
);
|
|
224
206
|
return;
|
|
225
207
|
}
|
|
@@ -257,7 +239,6 @@ function setupModelSideEffects(store, options, logger) {
|
|
|
257
239
|
unsubscribe();
|
|
258
240
|
save.flush();
|
|
259
241
|
isDestroyed = true;
|
|
260
|
-
save.cancel();
|
|
261
242
|
originalDestroy();
|
|
262
243
|
};
|
|
263
244
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-hox",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "一个轻量级、类型安全、零心智负担的 React Native 状态管理解决方案。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
-
"README.md"
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
41
42
|
],
|
|
42
43
|
"scripts": {
|
|
43
44
|
"test": "jest",
|
|
@@ -45,11 +46,12 @@
|
|
|
45
46
|
"prepack": "npm run build"
|
|
46
47
|
},
|
|
47
48
|
"dependencies": {
|
|
49
|
+
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
48
50
|
"use-sync-external-store": "^1.6.0"
|
|
49
51
|
},
|
|
50
52
|
"peerDependencies": {
|
|
51
53
|
"react": ">=16.8",
|
|
52
|
-
"react-native": ">=0.
|
|
54
|
+
"react-native": ">=0.65"
|
|
53
55
|
},
|
|
54
56
|
"devDependencies": {
|
|
55
57
|
"@types/jest": "^30.0.0",
|