as-model 0.1.19 → 0.1.21

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/README.md CHANGED
@@ -1,483 +1,483 @@
1
- [![npm][npm-image]][npm-url]
2
- [![NPM downloads][npm-downloads-image]][npm-url]
3
- [![standard][standard-image]][standard-url]
4
-
5
- [npm-image]: https://img.shields.io/npm/v/as-model.svg?style=flat-square
6
- [npm-url]: https://www.npmjs.com/package/as-model
7
- [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square
8
- [standard-url]: http://npm.im/standard
9
- [npm-downloads-image]: https://img.shields.io/npm/dm/as-model.svg?style=flat-square
10
-
11
-
12
- # as-model
13
-
14
- This is a simple state management library with model coding style for javascript/typescript.
15
-
16
- ## Code first
17
-
18
- Create a model function:
19
-
20
- ```js
21
- // model.js
22
- // A parameter for model state.
23
- export function counting(state){
24
- // Define instance object for outside usage.
25
- return {
26
- // Define properties for instance.
27
- count: state,
28
- symbol: !state? '': (state > 0? '+' : '-'),
29
- // Create action methods for changing state.
30
- increase:()=> state + 1,
31
- decrease:()=> state - 1,
32
- add(...additions){
33
- return additions.reduce((result, current)=>{
34
- return result + current;
35
- }, state);
36
- }
37
- };
38
- }
39
- ```
40
-
41
- Create store:
42
-
43
- ```js
44
- // store.js
45
- import {counting} from './model';
46
- import {createStore} from 'as-model';
47
-
48
- // Create and initialize a model store.
49
- const store = createStore(counting, 0);
50
- // Get instance and call action methods to change state.
51
- store.getInstance().increase();
52
- // Get new properties from instance.
53
- console.log(store.getInstance().count); // 1
54
- store.getInstance().add(2,3);
55
- console.log(store.getInstance().count); // 6
56
- ```
57
-
58
- Create multiple stores:
59
-
60
- ```js
61
- import {counting} from './model';
62
- import {createKey, createStores} from 'as-model';
63
-
64
- // Create model key with initial state.
65
- const countingKey0 = createKey(counting, 0);
66
- const countingKey1 = createKey(counting, 1);
67
-
68
- // Use model keys as templates to create multiple stores.
69
- const stores = createStores(countingKey0, countingKey1);
70
- // Find store by model key
71
- const store0 = stores.find(countingKey0);
72
- store0?.getInstance().increase();
73
- console.log(store0?.getInstance().count); // 1
74
- ```
75
-
76
- Model key is a template for creating multiple stores, and it is also an identifier to find the rightstore from multiple stores.
77
-
78
- Use **model** API to create store or key.
79
-
80
- ```js
81
- import {counting} from './model';
82
- import {model} from 'as-model';
83
-
84
- const store = model(counting).createStore(0);
85
- const key = model(counting).createKey(0);
86
- ......
87
- ```
88
-
89
- In typescript develop environment, `model` API can do a type check for making sure the model action method returns a correct type.
90
-
91
- ```js
92
- // ts
93
- import {model} from 'as-model';
94
-
95
- // The model api ensures every action method returns a same type value with model state.
96
- const counting = model((state: number)=>{
97
- return {
98
- count: state,
99
- increase:()=>state + 1 + '', // type error, should be number, but returns string.
100
- decrease:()=>state - 1,
101
- add(...additions: number[]){
102
- return additions.reduce((result, current)=>{
103
- return result + current;
104
- }, state);
105
- }
106
- };
107
- });
108
-
109
- const store = counting.createStore(0);
110
- const key = counting.createKey(0);
111
- ......
112
- ```
113
-
114
- Sync store
115
-
116
- ```js
117
- import {counting} from './model';
118
- import {model} from 'as-model';
119
-
120
- const store = model(counting).createStore(0);
121
- const {getInstance} = store;
122
- // Subscribe the state changes.
123
- const unsubscribe = store.subscribe((action)=>{
124
- console.log(store.getInstance());
125
- });
126
- getInstance().increase(); // output: {count: 1}
127
- // Destroy subscription.
128
- unsubscribe();
129
- ```
130
-
131
- Want to use async operations?
132
-
133
- ```js
134
- import {counting} from './model';
135
- import {model, createSelector} from 'as-model';
136
-
137
- const store = model(counting).select((getInstance)=>{
138
- const instance = getInstance();
139
- return {
140
- ...instance,
141
- async delayIncrease(){
142
- const ok = await new Promise((resolve)=>{
143
- setTimeout(()=>{
144
- resolve(true);
145
- },200);
146
- });
147
- if(ok){
148
- getInstance().increase();
149
- }
150
- }
151
- }
152
- }).createStore(0);
153
- const {subscribe, select} = createSelector(store);
154
- const unsubscribe = subscribe();
155
- // When use select with no parameter,
156
- // select method finds default selector for reproducing result
157
- const {delayIncrease} = select();
158
- await delayIncrease();
159
- select().count // 1
160
- ```
161
-
162
- Sync store with state in react hooks:
163
-
164
- ```js
165
- import {model, createStores} from 'as-model';
166
- import {
167
- createContext,
168
- useRef,
169
- useState,
170
- useEffect,
171
- useContext
172
- } from 'react';
173
-
174
- // Local state management
175
- function useModel(modelFn, defaultState){
176
- // Use ref to persist the store object.
177
- const storeRef = useRef(model(modelFn).createStore(defaultState));
178
- const store = storeRef.current;
179
- // Set store instance as an initial state for useState.
180
- const [state, setState] = useState(store.getInstance());
181
-
182
- useEffect(()=>{
183
- // Subscribe the state changes.
184
- const unsubscribe = store.subscribe((action)=>{
185
- setState(store.getInstance());
186
- });
187
- // Destroy subscription when component unmounts.
188
- return unsubscribe;
189
- }, []);
190
-
191
- return state;
192
- }
193
-
194
- function App(){
195
- const {
196
- count,
197
- increase
198
- } = useModel(counting, 0);
199
- }
200
-
201
- // global static state management
202
- function useModelStore(store){
203
- const [state, setState] = useState(store.getInstance());
204
-
205
- useEffect(()=>{
206
- const unsubscribe = store.subscribe((action)=>{
207
- setState(store.getInstance());
208
- });
209
- return unsubscribe;
210
- }, []);
211
-
212
- return state;
213
- }
214
-
215
- const store = model(counting).createStore({state: 0});
216
-
217
- function App(){
218
- const {
219
- count,
220
- increase
221
- } = useModelStore(store);
222
- }
223
-
224
- // global dynamic state management
225
- const ModelContext = createContext(null);
226
-
227
- function create(...keys){
228
- return {
229
- provide(Component){
230
- return function Provider(props){
231
- // Create and persist multiple stores in Component, that makes different elements from this Component carry different stores.
232
- const storesRef = useRef(createStores(...keys));
233
- const stores = storesRef.current;
234
- // Use Context to provide multiple stores to all the children.
235
- return (
236
- <ModelContext.Provider value={stores}>
237
- <Component {...props} />
238
- </ModelContext.Provider>
239
- );
240
- }
241
- }
242
- }
243
- }
244
-
245
- function useModelKey(key){
246
- const stores = useContext(ModelContext);
247
- if(stores==null){
248
- throw new Error('ModelContext is not provided');
249
- }
250
- // Use model key to find the right store.
251
- const store = stores.find(key);
252
- if(store==null){
253
- throw new Error('Can not find store by model key');
254
- }
255
- const [state, setState] = useState(store.getInstance());
256
-
257
- useEffect(()=>{
258
- const unsubscribe = store.subscribe((action)=>{
259
- setState(store.getInstance());
260
- });
261
- return unsubscribe;
262
- }, []);
263
-
264
- return state;
265
- }
266
-
267
- const countingKey = model(counting).createKey(0);
268
-
269
- const App = create(countingKey).provide(function App(){
270
- const {
271
- count,
272
- increase
273
- } = useModelKey(countingKey);
274
- });
275
- ```
276
-
277
- ## Install
278
-
279
- ```
280
- npm install as-model
281
- ```
282
-
283
- ## Simplify API
284
-
285
- ### createStore
286
-
287
- ```js
288
- function createStore(modelFnOrKey, initialState?):store
289
- ```
290
-
291
- #### parameters
292
-
293
- * modelFnOrKey - a model function accepts a state parameter and returns an object with action methods, or a model key of model function.
294
- * initialState - the initial state of the model.
295
-
296
- #### return
297
-
298
- A store object with model key and methods. The store object has `getInstance` method to get the instance of the model; `subscribe` method to subscribe the state changes; `update` method to update the store with new model function and initial state; `destroy` method to destroy the store.
299
-
300
- **store** structure:
301
-
302
- ```js
303
- {
304
- getInstance: ()=>instance,
305
- subscribe: (listener)=>unsubscribe,
306
- key: modelKey,
307
- destroy: ()=>void
308
- update: (
309
- data:{
310
- model?:modelFn,
311
- initialState?: any
312
- }
313
- )=>void
314
- }
315
- ```
316
-
317
- ### createKey
318
-
319
- ```js
320
- function createKey(modelFn, initialState?):key
321
- ```
322
-
323
- #### parameters
324
-
325
- * modelFn - a model function accepts a state parameter and returns an object with action methods.
326
- * initialState - the initial state of the model.
327
-
328
- #### return
329
-
330
- A model key function with `createStore` method to create a store with the model key.
331
-
332
- **key** structure:
333
-
334
- ```js
335
- {
336
- createStore: (initialState?)=>store
337
- }
338
- ```
339
-
340
- ### createStores
341
-
342
- ```js
343
- function createStores(...keys):stores
344
- ```
345
-
346
- #### parameters
347
-
348
- * keys - multiple model keys of model functions.
349
-
350
- #### return
351
-
352
- Multiple stores created by the model keys.
353
-
354
- **stores** structure:
355
-
356
- ```js
357
- {
358
- find: (key)=>store,
359
- destroy: ()=>void
360
- }
361
- ```
362
-
363
- ### createSignal
364
-
365
- ```js
366
- function createSignal(store):SignalStore
367
- ```
368
-
369
- #### parameters
370
-
371
- * store - a store object created by `createStore` method.
372
-
373
- #### return
374
-
375
- Signal store object with `subscribe` method to subscribe the state changes, and `getSignal` method to get the signal callback function.
376
-
377
- **signalAPI** structure:
378
-
379
- ```js
380
- {
381
- subscribe: (listener)=>unsubscribe,
382
- getSignal: ()=>signal,
383
- key: modelKey,
384
- }
385
- ```
386
-
387
- The signal function returns a real time instance from store. Only when the properties picked from real time instance are changed, the subscribed listener can receive an action notification.
388
-
389
- ### createSelector
390
-
391
- ```js
392
- function createSelector(store, opts?:SelectorOptions):SelectorStore
393
- ```
394
-
395
- #### parameters
396
-
397
- * store - a store object created by `createStore` method.
398
- * opts - (Optional) an object config to optimize createSelector.
399
-
400
- ```js
401
- {
402
- // When the selector is drived to reproduce a new data,
403
- // it compares if the result is different with the previous one,
404
- // if the camparing result is true, it represents no differ happens,
405
- // the subscribed callback will not be called.
406
- equality?: (current: T, next: T) => boolean;
407
- }
408
- ```
409
-
410
- #### return
411
-
412
- Selector store object with `subscribe` method to subscribe the state changes, and `select` method for reselecting instance.
413
-
414
- ```js
415
- {
416
- subscribe: (listener)=>unsubscribe,
417
- select: (selector?:(getInstance:()=>Instance)=>)=>any,
418
- key: modelKey,
419
- }
420
- ```
421
-
422
-
423
- ### model
424
-
425
- ```js
426
- function model(modelFn):modelAPI
427
- ```
428
-
429
- #### parameters
430
-
431
- * modelFn - a model function accepts a state parameter and returns an object with action methods.
432
-
433
- #### return
434
-
435
- Model api object with `createStore`, `createKey` methods to create store, key for the model function, and `select` method to set a default selector function (Use `createSelector(store).select()` to select the default one).
436
-
437
- **modelAPI** structure:
438
-
439
- ```js
440
- {
441
- createStore: (initialState?)=> store,
442
- createKey: (initialState?)=> key,
443
- select: (
444
- selector:(getInstance:()=>Instance)=>Record<string, any>|Array<any>
445
- )=>modelApI
446
- }
447
- ```
448
-
449
- ### config
450
-
451
- ```js
452
- function config(options):configAPI
453
- ```
454
-
455
- #### parameters
456
-
457
- * options - (Optional) an object with the following properties:
458
- * batchNotify - (Optional) a callback function to batch notify the listeners, for example: `unstable_batchedUpdates` from react-dom.
459
- * controlled - (Optional) a boolean state to tell as-model use controlled mode to output instance changes.
460
- * middleWares - (Optional) a middleWare array for reproducing state or ignore actions.
461
-
462
- #### return
463
-
464
- All apis above except `createSignal` and `createSelector` API.
465
-
466
- ```js
467
- {
468
- createStore: (modelFnOrKey, initialState?)=>store,
469
- createKey: (modelFn, initialState?)=>key,
470
- createStores: (...keys)=>stores,
471
- model: (modelFn)=>modelAPI
472
- }
473
- ```
474
-
475
- ## Browser Support
476
-
477
- ```
478
- chrome: '>=91',
479
- edge: '>=91',
480
- firefox: '=>90',
481
- safari: '>=15'
482
- ```
483
-
1
+ [![npm][npm-image]][npm-url]
2
+ [![NPM downloads][npm-downloads-image]][npm-url]
3
+ [![standard][standard-image]][standard-url]
4
+
5
+ [npm-image]: https://img.shields.io/npm/v/as-model.svg?style=flat-square
6
+ [npm-url]: https://www.npmjs.com/package/as-model
7
+ [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square
8
+ [standard-url]: http://npm.im/standard
9
+ [npm-downloads-image]: https://img.shields.io/npm/dm/as-model.svg?style=flat-square
10
+
11
+
12
+ # as-model
13
+
14
+ This is a simple state management library with model coding style for javascript/typescript.
15
+
16
+ ## Code first
17
+
18
+ Create a model function:
19
+
20
+ ```js
21
+ // model.js
22
+ // A parameter for model state.
23
+ export function counting(state){
24
+ // Define instance object for outside usage.
25
+ return {
26
+ // Define properties for instance.
27
+ count: state,
28
+ symbol: !state? '': (state > 0? '+' : '-'),
29
+ // Create action methods for changing state.
30
+ increase:()=> state + 1,
31
+ decrease:()=> state - 1,
32
+ add(...additions){
33
+ return additions.reduce((result, current)=>{
34
+ return result + current;
35
+ }, state);
36
+ }
37
+ };
38
+ }
39
+ ```
40
+
41
+ Create store:
42
+
43
+ ```js
44
+ // store.js
45
+ import {counting} from './model';
46
+ import {createStore} from 'as-model';
47
+
48
+ // Create and initialize a model store.
49
+ const store = createStore(counting, 0);
50
+ // Get instance and call action methods to change state.
51
+ store.getInstance().increase();
52
+ // Get new properties from instance.
53
+ console.log(store.getInstance().count); // 1
54
+ store.getInstance().add(2,3);
55
+ console.log(store.getInstance().count); // 6
56
+ ```
57
+
58
+ Create multiple stores:
59
+
60
+ ```js
61
+ import {counting} from './model';
62
+ import {createKey, createStores} from 'as-model';
63
+
64
+ // Create model key with initial state.
65
+ const countingKey0 = createKey(counting, 0);
66
+ const countingKey1 = createKey(counting, 1);
67
+
68
+ // Use model keys as templates to create multiple stores.
69
+ const stores = createStores(countingKey0, countingKey1);
70
+ // Find store by model key
71
+ const store0 = stores.find(countingKey0);
72
+ store0?.getInstance().increase();
73
+ console.log(store0?.getInstance().count); // 1
74
+ ```
75
+
76
+ Model key is a template for creating multiple stores, and it is also an identifier to find the rightstore from multiple stores.
77
+
78
+ Use **model** API to create store or key.
79
+
80
+ ```js
81
+ import {counting} from './model';
82
+ import {model} from 'as-model';
83
+
84
+ const store = model(counting).createStore(0);
85
+ const key = model(counting).createKey(0);
86
+ ......
87
+ ```
88
+
89
+ In typescript develop environment, `model` API can do a type check for making sure the model action method returns a correct type.
90
+
91
+ ```js
92
+ // ts
93
+ import {model} from 'as-model';
94
+
95
+ // The model api ensures every action method returns a same type value with model state.
96
+ const counting = model((state: number)=>{
97
+ return {
98
+ count: state,
99
+ increase:()=>state + 1 + '', // type error, should be number, but returns string.
100
+ decrease:()=>state - 1,
101
+ add(...additions: number[]){
102
+ return additions.reduce((result, current)=>{
103
+ return result + current;
104
+ }, state);
105
+ }
106
+ };
107
+ });
108
+
109
+ const store = counting.createStore(0);
110
+ const key = counting.createKey(0);
111
+ ......
112
+ ```
113
+
114
+ Sync store
115
+
116
+ ```js
117
+ import {counting} from './model';
118
+ import {model} from 'as-model';
119
+
120
+ const store = model(counting).createStore(0);
121
+ const {getInstance} = store;
122
+ // Subscribe the state changes.
123
+ const unsubscribe = store.subscribe((action)=>{
124
+ console.log(store.getInstance());
125
+ });
126
+ getInstance().increase(); // output: {count: 1}
127
+ // Destroy subscription.
128
+ unsubscribe();
129
+ ```
130
+
131
+ Want to use async operations?
132
+
133
+ ```js
134
+ import {counting} from './model';
135
+ import {model, createSelector} from 'as-model';
136
+
137
+ const store = model(counting).select((getInstance)=>{
138
+ const instance = getInstance();
139
+ return {
140
+ ...instance,
141
+ async delayIncrease(){
142
+ const ok = await new Promise((resolve)=>{
143
+ setTimeout(()=>{
144
+ resolve(true);
145
+ },200);
146
+ });
147
+ if(ok){
148
+ getInstance().increase();
149
+ }
150
+ }
151
+ }
152
+ }).createStore(0);
153
+ const {subscribe, select} = createSelector(store);
154
+ const unsubscribe = subscribe();
155
+ // When use select with no parameter,
156
+ // select method finds default selector for reproducing result
157
+ const {delayIncrease} = select();
158
+ await delayIncrease();
159
+ select().count // 1
160
+ ```
161
+
162
+ Sync store with state in react hooks:
163
+
164
+ ```js
165
+ import {model, createStores} from 'as-model';
166
+ import {
167
+ createContext,
168
+ useRef,
169
+ useState,
170
+ useEffect,
171
+ useContext
172
+ } from 'react';
173
+
174
+ // Local state management
175
+ function useModel(modelFn, defaultState){
176
+ // Use ref to persist the store object.
177
+ const storeRef = useRef(model(modelFn).createStore(defaultState));
178
+ const store = storeRef.current;
179
+ // Set store instance as an initial state for useState.
180
+ const [state, setState] = useState(store.getInstance());
181
+
182
+ useEffect(()=>{
183
+ // Subscribe the state changes.
184
+ const unsubscribe = store.subscribe((action)=>{
185
+ setState(store.getInstance());
186
+ });
187
+ // Destroy subscription when component unmounts.
188
+ return unsubscribe;
189
+ }, []);
190
+
191
+ return state;
192
+ }
193
+
194
+ function App(){
195
+ const {
196
+ count,
197
+ increase
198
+ } = useModel(counting, 0);
199
+ }
200
+
201
+ // global static state management
202
+ function useModelStore(store){
203
+ const [state, setState] = useState(store.getInstance());
204
+
205
+ useEffect(()=>{
206
+ const unsubscribe = store.subscribe((action)=>{
207
+ setState(store.getInstance());
208
+ });
209
+ return unsubscribe;
210
+ }, []);
211
+
212
+ return state;
213
+ }
214
+
215
+ const store = model(counting).createStore({state: 0});
216
+
217
+ function App(){
218
+ const {
219
+ count,
220
+ increase
221
+ } = useModelStore(store);
222
+ }
223
+
224
+ // global dynamic state management
225
+ const ModelContext = createContext(null);
226
+
227
+ function create(...keys){
228
+ return {
229
+ provide(Component){
230
+ return function Provider(props){
231
+ // Create and persist multiple stores in Component, that makes different elements from this Component carry different stores.
232
+ const storesRef = useRef(createStores(...keys));
233
+ const stores = storesRef.current;
234
+ // Use Context to provide multiple stores to all the children.
235
+ return (
236
+ <ModelContext.Provider value={stores}>
237
+ <Component {...props} />
238
+ </ModelContext.Provider>
239
+ );
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ function useModelKey(key){
246
+ const stores = useContext(ModelContext);
247
+ if(stores==null){
248
+ throw new Error('ModelContext is not provided');
249
+ }
250
+ // Use model key to find the right store.
251
+ const store = stores.find(key);
252
+ if(store==null){
253
+ throw new Error('Can not find store by model key');
254
+ }
255
+ const [state, setState] = useState(store.getInstance());
256
+
257
+ useEffect(()=>{
258
+ const unsubscribe = store.subscribe((action)=>{
259
+ setState(store.getInstance());
260
+ });
261
+ return unsubscribe;
262
+ }, []);
263
+
264
+ return state;
265
+ }
266
+
267
+ const countingKey = model(counting).createKey(0);
268
+
269
+ const App = create(countingKey).provide(function App(){
270
+ const {
271
+ count,
272
+ increase
273
+ } = useModelKey(countingKey);
274
+ });
275
+ ```
276
+
277
+ ## Install
278
+
279
+ ```
280
+ npm install as-model
281
+ ```
282
+
283
+ ## Simplify API
284
+
285
+ ### createStore
286
+
287
+ ```js
288
+ function createStore(modelFnOrKey, initialState?):store
289
+ ```
290
+
291
+ #### parameters
292
+
293
+ * modelFnOrKey - a model function accepts a state parameter and returns an object with action methods, or a model key of model function.
294
+ * initialState - the initial state of the model.
295
+
296
+ #### return
297
+
298
+ A store object with model key and methods. The store object has `getInstance` method to get the instance of the model; `subscribe` method to subscribe the state changes; `update` method to update the store with new model function and initial state; `destroy` method to destroy the store.
299
+
300
+ **store** structure:
301
+
302
+ ```js
303
+ {
304
+ getInstance: ()=>instance,
305
+ subscribe: (listener)=>unsubscribe,
306
+ key: modelKey,
307
+ destroy: ()=>void
308
+ update: (
309
+ data:{
310
+ model?:modelFn,
311
+ initialState?: any
312
+ }
313
+ )=>void
314
+ }
315
+ ```
316
+
317
+ ### createKey
318
+
319
+ ```js
320
+ function createKey(modelFn, initialState?):key
321
+ ```
322
+
323
+ #### parameters
324
+
325
+ * modelFn - a model function accepts a state parameter and returns an object with action methods.
326
+ * initialState - the initial state of the model.
327
+
328
+ #### return
329
+
330
+ A model key function with `createStore` method to create a store with the model key.
331
+
332
+ **key** structure:
333
+
334
+ ```js
335
+ {
336
+ createStore: (initialState?)=>store
337
+ }
338
+ ```
339
+
340
+ ### createStores
341
+
342
+ ```js
343
+ function createStores(...keys):stores
344
+ ```
345
+
346
+ #### parameters
347
+
348
+ * keys - multiple model keys of model functions.
349
+
350
+ #### return
351
+
352
+ Multiple stores created by the model keys.
353
+
354
+ **stores** structure:
355
+
356
+ ```js
357
+ {
358
+ find: (key)=>store,
359
+ destroy: ()=>void
360
+ }
361
+ ```
362
+
363
+ ### createSignal
364
+
365
+ ```js
366
+ function createSignal(store):SignalStore
367
+ ```
368
+
369
+ #### parameters
370
+
371
+ * store - a store object created by `createStore` method.
372
+
373
+ #### return
374
+
375
+ Signal store object with `subscribe` method to subscribe the state changes, and `getSignal` method to get the signal callback function.
376
+
377
+ **signalAPI** structure:
378
+
379
+ ```js
380
+ {
381
+ subscribe: (listener)=>unsubscribe,
382
+ getSignal: ()=>signal,
383
+ key: modelKey,
384
+ }
385
+ ```
386
+
387
+ The signal function returns a real time instance from store. Only when the properties picked from real time instance are changed, the subscribed listener can receive an action notification.
388
+
389
+ ### createSelector
390
+
391
+ ```js
392
+ function createSelector(store, opts?:SelectorOptions):SelectorStore
393
+ ```
394
+
395
+ #### parameters
396
+
397
+ * store - a store object created by `createStore` method.
398
+ * opts - (Optional) an object config to optimize createSelector.
399
+
400
+ ```js
401
+ {
402
+ // When the selector is drived to reproduce a new data,
403
+ // it compares if the result is different with the previous one,
404
+ // if the camparing result is true, it represents no differ happens,
405
+ // the subscribed callback will not be called.
406
+ equality?: (current: T, next: T) => boolean;
407
+ }
408
+ ```
409
+
410
+ #### return
411
+
412
+ Selector store object with `subscribe` method to subscribe the state changes, and `select` method for reselecting instance.
413
+
414
+ ```js
415
+ {
416
+ subscribe: (listener)=>unsubscribe,
417
+ select: (selector?:(getInstance:()=>Instance)=>)=>any,
418
+ key: modelKey,
419
+ }
420
+ ```
421
+
422
+
423
+ ### model
424
+
425
+ ```js
426
+ function model(modelFn):modelAPI
427
+ ```
428
+
429
+ #### parameters
430
+
431
+ * modelFn - a model function accepts a state parameter and returns an object with action methods.
432
+
433
+ #### return
434
+
435
+ Model api object with `createStore`, `createKey` methods to create store, key for the model function, and `select` method to set a default selector function (Use `createSelector(store).select()` to select the default one).
436
+
437
+ **modelAPI** structure:
438
+
439
+ ```js
440
+ {
441
+ createStore: (initialState?)=> store,
442
+ createKey: (initialState?)=> key,
443
+ select: (
444
+ selector:(getInstance:()=>Instance)=>Record<string, any>|Array<any>
445
+ )=>modelApI
446
+ }
447
+ ```
448
+
449
+ ### config
450
+
451
+ ```js
452
+ function config(options):configAPI
453
+ ```
454
+
455
+ #### parameters
456
+
457
+ * options - (Optional) an object with the following properties:
458
+ * batchNotify - (Optional) a callback function to batch notify the listeners, for example: `unstable_batchedUpdates` from react-dom.
459
+ * controlled - (Optional) a boolean state to tell as-model use controlled mode to output instance changes.
460
+ * middleWares - (Optional) a middleWare array for reproducing state or ignore actions.
461
+
462
+ #### return
463
+
464
+ All apis above except `createSignal` and `createSelector` API.
465
+
466
+ ```js
467
+ {
468
+ createStore: (modelFnOrKey, initialState?)=>store,
469
+ createKey: (modelFn, initialState?)=>key,
470
+ createStores: (...keys)=>stores,
471
+ model: (modelFn)=>modelAPI
472
+ }
473
+ ```
474
+
475
+ ## Browser Support
476
+
477
+ ```
478
+ chrome: '>=91',
479
+ edge: '>=91',
480
+ firefox: '=>90',
481
+ safari: '>=15'
482
+ ```
483
+
package/dist/index.js CHANGED
@@ -604,7 +604,10 @@
604
604
  updater.mutate(function(u, effect) {
605
605
  var _args_model;
606
606
  var model2 = (_args_model = args.model) !== null && _args_model !== void 0 ? _args_model : u.model;
607
- var state = "state" in args ? args.state : u.state;
607
+ var hasState = Object.prototype.hasOwnProperty.call(args, "state");
608
+ var hasInitialState = Object.prototype.hasOwnProperty.call(args, "initialState");
609
+ var state = hasState ? args.state : u.state;
610
+ var isInitialize = !u.initialized || hasInitialState;
608
611
  var token = createToken();
609
612
  if (u.controlled) {
610
613
  var instance = model2(state);
@@ -620,8 +623,9 @@
620
623
  if (!u.initialized && !("state" in args)) {
621
624
  throw new Error("The updater has not been initialized, it should be updated with a state for initializing.");
622
625
  }
623
- if (!u.initialized) {
624
- var instance1 = model2(state);
626
+ if (isInitialize) {
627
+ var initialState = hasInitialState ? args.initialState : state;
628
+ var instance1 = model2(initialState);
625
629
  var initializedUpdater = createInitializedUpdater(u, middleWare);
626
630
  return _object_spread(_object_spread_props(_object_spread({}, u), {
627
631
  model: model2,
@@ -634,22 +638,18 @@
634
638
  if (Object.is(u.model, model2) && Object.is(u.state, state)) {
635
639
  return u;
636
640
  }
637
- var instance2 = model2(state);
638
641
  effect(function(up) {
639
642
  up.notify({
640
643
  type: null,
641
644
  method: null,
642
645
  prevInstance: u.instance,
643
- instance: instance2,
646
+ instance: u.instance,
644
647
  prevState: u.state,
645
648
  state
646
649
  });
647
650
  });
648
651
  return _object_spread_props(_object_spread({}, u), {
649
- state,
650
652
  model: model2,
651
- instance: instance2,
652
- token,
653
653
  initialized: true,
654
654
  cacheFields: {},
655
655
  cacheMethods: {}
@@ -1198,7 +1198,7 @@
1198
1198
  getInstance,
1199
1199
  update: function update(args) {
1200
1200
  var updateArgs = args !== null && args !== void 0 ? args : {};
1201
- if ("key" in updateArgs && updateArgs.key) {
1201
+ if (updateArgs.key) {
1202
1202
  var updatingKey = updateArgs.key, updatingModel = updateArgs.model, rest = _object_without_properties(updateArgs, [
1203
1203
  "key",
1204
1204
  "model"
@@ -1209,7 +1209,7 @@
1209
1209
  store.key = updatingKey;
1210
1210
  return;
1211
1211
  }
1212
- if ("model" in updateArgs && updateArgs.model) {
1212
+ if (updateArgs.model) {
1213
1213
  var updatingKey1 = updateArgs.key, updatingModel1 = updateArgs.model, rest1 = _object_without_properties(updateArgs, [
1214
1214
  "key",
1215
1215
  "model"
@@ -103,13 +103,13 @@ function createStore(modelLike, config = {}) {
103
103
  getInstance,
104
104
  update(args) {
105
105
  const updateArgs = args != null ? args : {};
106
- if ("key" in updateArgs && updateArgs.key) {
106
+ if (updateArgs.key) {
107
107
  const _a = updateArgs, { key: updatingKey, model: updatingModel } = _a, rest = __objRest(_a, ["key", "model"]);
108
108
  updater.update(__spreadProps(__spreadValues({}, rest), { model: updatingKey.source }));
109
109
  store.key = updatingKey;
110
110
  return;
111
111
  }
112
- if ("model" in updateArgs && updateArgs.model) {
112
+ if (updateArgs.model) {
113
113
  const _b = updateArgs, { key: updatingKey, model: updatingModel } = _b, rest = __objRest(_b, ["key", "model"]);
114
114
  updater.update(__spreadProps(__spreadValues({}, rest), { model: updatingModel }));
115
115
  store.key = createPrimaryKey(updatingModel, config);
@@ -37,11 +37,17 @@ function createUpdateFn(updater, middleWare) {
37
37
  updater.mutate((u, effect) => {
38
38
  var _a;
39
39
  const model = (_a = args.model) != null ? _a : u.model;
40
- const state = "state" in args ? args.state : u.state;
40
+ const hasState = Object.prototype.hasOwnProperty.call(args, "state");
41
+ const hasInitialState = Object.prototype.hasOwnProperty.call(
42
+ args,
43
+ "initialState"
44
+ );
45
+ const state = hasState ? args.state : u.state;
46
+ const isInitialize = !u.initialized || hasInitialState;
41
47
  const token = createToken();
42
48
  if (u.controlled) {
43
- const instance2 = model(state);
44
- return __spreadProps(__spreadValues({}, u), { state, instance: instance2, model });
49
+ const instance = model(state);
50
+ return __spreadProps(__spreadValues({}, u), { state, instance, model });
45
51
  }
46
52
  if (u.isDestroyed) {
47
53
  return u;
@@ -51,13 +57,14 @@ function createUpdateFn(updater, middleWare) {
51
57
  "The updater has not been initialized, it should be updated with a state for initializing."
52
58
  );
53
59
  }
54
- if (!u.initialized) {
55
- const instance2 = model(state);
60
+ if (isInitialize) {
61
+ const initialState = hasInitialState ? args.initialState : state;
62
+ const instance = model(initialState);
56
63
  const initializedUpdater = createInitializedUpdater(u, middleWare);
57
64
  return __spreadValues(__spreadProps(__spreadValues({}, u), {
58
65
  model,
59
66
  state,
60
- instance: instance2,
67
+ instance,
61
68
  initialized: true,
62
69
  token
63
70
  }), initializedUpdater);
@@ -65,22 +72,18 @@ function createUpdateFn(updater, middleWare) {
65
72
  if (Object.is(u.model, model) && Object.is(u.state, state)) {
66
73
  return u;
67
74
  }
68
- const instance = model(state);
69
75
  effect((up) => {
70
76
  up.notify({
71
77
  type: null,
72
78
  method: null,
73
79
  prevInstance: u.instance,
74
- instance,
80
+ instance: u.instance,
75
81
  prevState: u.state,
76
82
  state
77
83
  });
78
84
  });
79
85
  return __spreadProps(__spreadValues({}, u), {
80
- state,
81
86
  model,
82
- instance,
83
- token,
84
87
  initialized: true,
85
88
  cacheFields: {},
86
89
  cacheMethods: {}
package/index.d.ts CHANGED
@@ -100,7 +100,12 @@ export declare interface Store<
100
100
  subscribe: (dispatcher: Dispatch) => () => void;
101
101
  getToken: () => Token;
102
102
  getInstance: () => T;
103
- update: (args?: { model?: Model<S, T>; initialState?: S; state?: S }) => void;
103
+ update: (args?: {
104
+ model?: Model<S, T>;
105
+ key?: Key<S, T, R>;
106
+ initialState?: S;
107
+ state?: S;
108
+ }) => void;
104
109
  destroy: () => void;
105
110
  payload: <P>(
106
111
  callback?: (payload: P | undefined) => P | undefined
package/package.json CHANGED
@@ -1,68 +1,68 @@
1
- {
2
- "private": false,
3
- "name": "as-model",
4
- "version": "0.1.19",
5
- "description": "This is a model state management tool",
6
- "license": "MIT",
7
- "author": "Jimmy.Harding",
8
- "homepage": "https://github.com/filefoxper/a-model",
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/filefoxper/a-model"
12
- },
13
- "main": "dist/index.js",
14
- "module": "esm/index.js",
15
- "files": [
16
- "dist",
17
- "esm",
18
- "index.d.ts"
19
- ],
20
- "scripts": {
21
- "build": "node ./build.js",
22
- "docs": "docsify serve ./docs",
23
- "lint": "eslint src --fix --ext .ts,.tsx ",
24
- "lint-init": "eslint --init",
25
- "test": "jest --coverage"
26
- },
27
- "typings": "index.d.ts",
28
- "devDependencies": {
29
- "@types/jest": "^29.5.14",
30
- "@babel/cli": "^7.27.2",
31
- "@babel/core": "^7.27.4",
32
- "@babel/eslint-parser": "^7.27.5",
33
- "@babel/plugin-transform-runtime": "^7.27.4",
34
- "@babel/preset-env": "^7.27.2",
35
- "@babel/preset-typescript": "^7.27.1",
36
- "@babel/runtime": "^7.27.6",
37
- "esbuild": "^0.25.0",
38
- "esbuild-plugin-es5": "2.1.1",
39
- "eslint": "^8.49.0",
40
- "eslint-config-airbnb": "^19.0.4",
41
- "eslint-config-airbnb-typescript": "^17.1.0",
42
- "eslint-config-prettier": "^9.0.0",
43
- "eslint-import-resolver-typescript": "^3.6.0",
44
- "eslint-plugin-import": "^2.28.1",
45
- "eslint-plugin-jsx-a11y": "^6.7.1",
46
- "eslint-plugin-prettier": "^5.0.0",
47
- "eslint-plugin-react": "^7.33.2",
48
- "eslint-plugin-react-hooks": "^4.6.0",
49
- "eslint-plugin-unused-imports": "^2.0.0",
50
- "eslint-webpack-plugin": "^4.0.1",
51
- "prettier": "^3.0.3",
52
- "prettier-eslint": "^15.0.1",
53
- "prettier-eslint-cli": "^7.1.0",
54
- "typescript": "^4.9.5",
55
- "babel-jest": "29.7.0",
56
- "jest": "29.7.0",
57
- "jest-environment-jsdom": "29.7.0",
58
- "ts-node": "^10.8.1",
59
- "pmnps": "^4.5.3",
60
- "@pmnps/plugin-publish": "4.5.0"
61
- },
62
- "keywords": [
63
- "model",
64
- "state",
65
- "state-management",
66
- "typescript"
67
- ]
68
- }
1
+ {
2
+ "private": false,
3
+ "name": "as-model",
4
+ "version": "0.1.21",
5
+ "description": "This is a model state management tool",
6
+ "license": "MIT",
7
+ "author": "Jimmy.Harding",
8
+ "homepage": "https://github.com/filefoxper/a-model",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/filefoxper/a-model"
12
+ },
13
+ "main": "dist/index.js",
14
+ "module": "esm/index.js",
15
+ "files": [
16
+ "dist",
17
+ "esm",
18
+ "index.d.ts"
19
+ ],
20
+ "scripts": {
21
+ "build": "node ./build.js",
22
+ "docs": "docsify serve ./docs",
23
+ "lint": "eslint src --fix --ext .ts,.tsx ",
24
+ "lint-init": "eslint --init",
25
+ "test": "jest --coverage"
26
+ },
27
+ "typings": "index.d.ts",
28
+ "devDependencies": {
29
+ "@types/jest": "^29.5.14",
30
+ "@babel/cli": "^7.27.2",
31
+ "@babel/core": "^7.27.4",
32
+ "@babel/eslint-parser": "^7.27.5",
33
+ "@babel/plugin-transform-runtime": "^7.27.4",
34
+ "@babel/preset-env": "^7.27.2",
35
+ "@babel/preset-typescript": "^7.27.1",
36
+ "@babel/runtime": "^7.27.6",
37
+ "esbuild": "^0.25.0",
38
+ "esbuild-plugin-es5": "2.1.1",
39
+ "eslint": "^8.49.0",
40
+ "eslint-config-airbnb": "^19.0.4",
41
+ "eslint-config-airbnb-typescript": "^17.1.0",
42
+ "eslint-config-prettier": "^9.0.0",
43
+ "eslint-import-resolver-typescript": "^3.6.0",
44
+ "eslint-plugin-import": "^2.28.1",
45
+ "eslint-plugin-jsx-a11y": "^6.7.1",
46
+ "eslint-plugin-prettier": "^5.0.0",
47
+ "eslint-plugin-react": "^7.33.2",
48
+ "eslint-plugin-react-hooks": "^4.6.0",
49
+ "eslint-plugin-unused-imports": "^2.0.0",
50
+ "eslint-webpack-plugin": "^4.0.1",
51
+ "prettier": "^3.0.3",
52
+ "prettier-eslint": "^15.0.1",
53
+ "prettier-eslint-cli": "^7.1.0",
54
+ "typescript": "^4.9.5",
55
+ "babel-jest": "29.7.0",
56
+ "jest": "29.7.0",
57
+ "jest-environment-jsdom": "29.7.0",
58
+ "ts-node": "^10.8.1",
59
+ "pmnps": "^4.5.3",
60
+ "@pmnps/plugin-publish": "4.5.0"
61
+ },
62
+ "keywords": [
63
+ "model",
64
+ "state",
65
+ "state-management",
66
+ "typescript"
67
+ ]
68
+ }