@chhsiao1981/use-thunk 11.0.0 → 13.0.0

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.
Files changed (85) hide show
  1. package/README.md +166 -99
  2. package/package.json +10 -16
  3. package/src/action/ActionOrThunk.ts +1 -1
  4. package/src/action/index.ts +3 -3
  5. package/src/action/thunk.ts +10 -2
  6. package/src/defaultThunks/index.ts +15 -0
  7. package/src/defaultThunks/init/index.ts +27 -0
  8. package/src/{init → defaultThunks/init}/initCore.ts +2 -2
  9. package/src/{remove.ts → defaultThunks/remove.ts} +2 -2
  10. package/src/{setDefaultID.ts → defaultThunks/setDefaultID.ts} +4 -3
  11. package/src/{update.ts → defaultThunks/update.ts} +2 -7
  12. package/src/defaultThunks/upsert.ts +27 -0
  13. package/src/dispatch.ts +7 -0
  14. package/src/get.ts +4 -0
  15. package/src/index.ts +43 -31
  16. package/src/reducer/createReducer.ts +18 -0
  17. package/src/reducer/defaultReduceMap.ts +26 -0
  18. package/src/{reducer.ts → reducer/index.ts} +5 -2
  19. package/src/reducer/reduceMap.ts +6 -0
  20. package/src/{createThunk.ts → registerThunk.ts} +11 -13
  21. package/src/set/index.ts +9 -0
  22. package/src/{setMap.ts → set/setMap.ts} +12 -18
  23. package/src/states/index.ts +84 -0
  24. package/src/{stateTypes.ts → states/types.ts} +7 -7
  25. package/src/thunkContext/index.ts +9 -0
  26. package/src/{thunkContextMap.ts → thunkContext/thunkContextMap.ts} +2 -2
  27. package/src/{thunkContextTypes.ts → thunkContext/types.ts} +1 -1
  28. package/src/thunkModule/defaultDoModule.ts +13 -0
  29. package/src/thunkModule/index.ts +12 -12
  30. package/src/{useThunk.ts → useThunk/index.ts} +7 -10
  31. package/src/{useThunkReducer.ts → useThunk/useThunkReducer.ts} +50 -10
  32. package/src/utils/deepCopy.ts +30 -0
  33. package/src/utils/genID.ts +6 -0
  34. package/src/utils/index.ts +3 -0
  35. package/types/action/ActionOrThunk.d.ts +1 -1
  36. package/types/action/index.d.ts +2 -2
  37. package/types/action/thunk.d.ts +4 -2
  38. package/types/defaultThunks/index.d.ts +10 -0
  39. package/types/defaultThunks/init/index.d.ts +9 -0
  40. package/types/{init → defaultThunks/init}/initCore.d.ts +2 -2
  41. package/types/{remove.d.ts → defaultThunks/remove.d.ts} +2 -2
  42. package/types/{setDefaultID.d.ts → defaultThunks/setDefaultID.d.ts} +2 -2
  43. package/types/{update.d.ts → defaultThunks/update.d.ts} +2 -3
  44. package/types/defaultThunks/upsert.d.ts +5 -0
  45. package/types/dispatch.d.ts +4 -4
  46. package/types/get.d.ts +3 -0
  47. package/types/index.d.ts +11 -20
  48. package/types/reducer/createReducer.d.ts +3 -0
  49. package/types/reducer/defaultReduceMap.d.ts +2 -0
  50. package/types/{reducer.d.ts → reducer/index.d.ts} +4 -2
  51. package/types/reducer/reduceMap.d.ts +5 -0
  52. package/types/{createThunk.d.ts → registerThunk.d.ts} +1 -2
  53. package/types/set/index.d.ts +5 -0
  54. package/types/set/setMap.d.ts +16 -0
  55. package/types/states/index.d.ts +10 -0
  56. package/types/{stateTypes.d.ts → states/types.d.ts} +6 -6
  57. package/types/thunkContext/index.d.ts +6 -0
  58. package/types/{thunkContextMap.d.ts → thunkContext/thunkContextMap.d.ts} +2 -2
  59. package/types/{thunkContextTypes.d.ts → thunkContext/types.d.ts} +1 -1
  60. package/types/thunkModule/defaultDoModule.d.ts +5 -0
  61. package/types/thunkModule/index.d.ts +8 -11
  62. package/types/useThunk/index.d.ts +9 -0
  63. package/types/{useThunkReducer.d.ts → useThunk/useThunkReducer.d.ts} +3 -3
  64. package/types/utils/deepCopy.d.ts +2 -0
  65. package/types/utils/genID.d.ts +1 -0
  66. package/types/utils/index.d.ts +3 -0
  67. package/src/createReducer.ts +0 -24
  68. package/src/genUUID.ts +0 -38
  69. package/src/init/index.ts +0 -26
  70. package/src/reduceMap.ts +0 -22
  71. package/src/set.ts +0 -7
  72. package/src/states.ts +0 -47
  73. package/src/thunkModule/defaultThunkModuleFuncMap.ts +0 -16
  74. package/types/createReducer.d.ts +0 -4
  75. package/types/dispatchFuncMap.d.ts +0 -17
  76. package/types/genUUID.d.ts +0 -1
  77. package/types/init/index.d.ts +0 -7
  78. package/types/reduceMap.d.ts +0 -6
  79. package/types/set.d.ts +0 -4
  80. package/types/setMap.d.ts +0 -17
  81. package/types/states.d.ts +0 -9
  82. package/types/thunkModule/defaultThunkModuleFuncMap.d.ts +0 -5
  83. package/types/useThunk.d.ts +0 -9
  84. /package/src/{ThunkContext.tsx → thunkContext/ThunkContext.tsx} +0 -0
  85. /package/types/{ThunkContext.d.ts → thunkContext/ThunkContext.d.ts} +0 -0
package/README.md CHANGED
@@ -2,39 +2,28 @@
2
2
 
3
3
  [![codecov](https://codecov.io/gh/chhsiao1981/use-thunk/branch/main/graph/badge.svg)](https://codecov.io/gh/chhsiao1981/use-thunk)
4
4
 
5
- A framework easily using `useThunk` to manage the data-state.
5
+ A framework for easily managing global data state with `useThunk`, with [`zustand`](https://github.com/pmndrs/zustand)-like taste.
6
6
 
7
- Adopted concept of [redux-thunk](https://redux.js.org/usage/writing-logic-thunks) and [redux-duck](https://github.com/PlatziDev/redux-duck)
7
+ Inspired by the concepts of [Redux Thunk](https://redux.js.org/usage/writing-logic-thunks) and [Redux Duck](https://github.com/PlatziDev/redux-duck).
8
8
 
9
- [src/useThunkReducer.ts](src/useThunkReducer.ts) is adopted from [nathanbuchar/react-hook-thunk-reducer](https://github.com/nathanbuchar/react-hook-thunk-reducer/blob/master/src/thunk-reducer.js).
9
+ [src/useThunkReducer.ts](src/useThunkReducer.ts) is adapted from [nathanbuchar/react-hook-thunk-reducer](https://github.com/nathanbuchar/react-hook-thunk-reducer/blob/master/src/thunk-reducer.js).
10
10
 
11
- `use-thunk` is with the following additional features:
11
+ For more information, please check [docs/00-introduction.md](docs/00-introduction.md).
12
12
 
13
- 1. The development of the thunk modules follows the concept of `redux-duck`.
14
- 2. Instead of action / reducer, we focus only on `thunk`, and have the primitive actions/reducers.
15
-
16
- Please check [docs/00-introduction.md](docs/00-introduction.md) for more information.
17
-
18
- Please check [demo-use-thunk](https://github.com/chhsiao1981/demo-use-thunk) for a demo to use Thunk.
19
-
20
- ### Breaking Changes
21
-
22
- * Starting from `10.0.0`: The ClassState is shared globally, with `registerThunk` and `ThunkContext`.
23
- * Starting from `9.0.0`: npm package is renamed as [@chhsiao1981/use-thunk](https://www.npmjs.com/package/%40chhsiao1981/use-thunk)
24
- * Starting from `8.0.0`: [Totally renamed as `useThunk`](https://github.com/chhsiao1981/use-thunk/issues/105).
13
+ For usage examples, please refer to [demo-use-thunk](https://github.com/chhsiao1981/demo-use-thunk).
25
14
 
26
15
  ## Install
27
16
 
28
17
  npm install --save @chhsiao1981/use-thunk
29
18
 
30
- ## Example
19
+ ## Getting Started
31
20
 
32
- Thunk module able to do increment (reducers/increment.ts):
21
+ Thunk module able to do increment (thunks/increment.ts):
33
22
 
34
23
  ```ts
35
- import { init as _init, setData, Thunk, getState, type State as rState, genUUID } from '@chhsiao1981/use-thunk'
24
+ import { type Thunk, type State as rState, update } from '@chhsiao1981/use-thunk'
36
25
 
37
- export const myClass = 'demo/Increment'
26
+ export const name = 'demo/Increment'
38
27
 
39
28
  export interface State extends rState {
40
29
  count: number
@@ -44,54 +33,53 @@ export const defaultState: State = {
44
33
  count: 0
45
34
  }
46
35
 
47
- export const init = (): Thunk<State> => {
48
- const myID = genUUID()
49
- return async (dispatch, getClassState) => {
50
- dispatch(_init({myID, state: defaultState}))
36
+ // upsert directly with set.
37
+ export const increment = (myID: string, num: number = 1): Thunk<State> => {
38
+ return async (set, get) => {
39
+ let me = get(myID)
40
+ const {count} = me
41
+
42
+ set(myID, { count: count + num })
51
43
  }
52
44
  }
53
45
 
54
- export const increment = (myID: string): Thunk<State> => {
55
- return async (dispatch, getClassState) => {
56
- let classState = getClassState()
57
- let me = getState(classState, myID)
58
- if(!me) {
59
- return
60
- }
46
+ // or we can treat set as dispatching a base action.
47
+ export const increment2 = (myID: string): Thunk<State> => {
48
+ return async (set, get) => {
49
+ let me = get(myID)
50
+ const {count} = me
51
+
52
+ set(update({ count: count + 2 }))
53
+ }
54
+ }
61
55
 
62
- dispatch(setData(myID, { count: me.count + 1 }))
56
+ // or we can use set as dispatching a thunk function.
57
+ export const increment3 = (myID: string): Thunk<State> => {
58
+ return async (set) => {
59
+ set(increment(myID, 3))
63
60
  }
64
61
  }
65
62
  ```
66
63
 
67
- App.tsx:
64
+ components/App.tsx:
68
65
 
69
66
  ```tsx
70
- import { type ThunkModuleToFunc, useThunk, getDefaultID, getState } from '@chhsiao1981/use-thunk'
71
- import * as DoIncrement from './reducers/increment'
72
-
73
- type TDoIncrement = ThunkModuleToFunc(typeof DoIncrement)
74
-
75
- type Props = {
76
- }
67
+ import { type ThunkModuleToFunc, useThunk, getState } from '@chhsiao1981/use-thunk'
68
+ import * as DoIncrement from './thunks/increment'
77
69
 
78
- export default (props: Props) => {
79
- const [classStateIncrement, doIncrement] = useThunk<DoIncrement.State, TDoIncrement>(DoIncrement, StateType.LOCAL)
70
+ type doIncrement = toDoModule(typeof DoIncrement)
80
71
 
81
- //init
82
- useEffect(() => {
83
- doIncrement.init()
84
- }, [])
85
-
86
- // states
87
- const incrementID = getDefaultID(classStateIncrement)
88
- const increment = getState(classStateIncrement) || DoIncrement.defaultState
72
+ export default () => {
73
+ const useIncrement = useThunk<DoIncrement.State, doIncrement>(DoIncrement)
74
+ const [increment, doIncrement, incrementID] = getState(useIncrement)
89
75
 
90
76
  // to render
91
77
  return (
92
78
  <div>
93
79
  <p>count: {increment.count}</p>
94
- <button onClick={() => doIncrement.increment(incrementID)}>increase</button>
80
+ <button onClick={() => doIncrement.increment(incrementID)}>increase 1</button>
81
+ <button onClick={() => doIncrement.increment2(incrementID)}>increase 2</button>
82
+ <button onClick={() => doIncrement.increment3(incrementID)}>increase 3</button>
95
83
  </div>
96
84
  )
97
85
  }
@@ -102,8 +90,8 @@ main.tsx:
102
90
  import { registerThunk, ThunkContext } from "@chhsiao1981/use-thunk";
103
91
  import { StrictMode } from "react";
104
92
  import { createRoot } from "react-dom/client";
105
- import * as DoIncrement from './reducers/increment'
106
- import App from "./App.tsx";
93
+ import * as DoIncrement from './thunks/increment'
94
+ import App from "./components/App";
107
95
 
108
96
  registerThunk(DoIncrement)
109
97
 
@@ -116,47 +104,54 @@ createRoot(document.getElementById("root")!).render(
116
104
  )
117
105
  ```
118
106
 
107
+ ## Introduction
108
+
109
+
110
+
111
+ ## Development Pattern
119
112
  ### Must Included in a Thunk Module
120
113
 
121
114
  ```ts
122
115
  import type { State as rState } from '@chhsiao1981/use-thunk'
123
116
 
124
- // reducer class name.
125
- export const myClass = ""
117
+ // Thunk-module name.
118
+ export const name = ""
126
119
 
127
120
  // state definition of the reducer.
128
121
  export interface State extends rState {
129
122
  }
130
123
 
124
+ export const defaultState: State = {}
125
+
131
126
  .
132
127
  .
133
128
  .
134
129
  ```
135
130
 
136
- ### Must Included in a Top-level Component
131
+ ### Must Included in a Statically-allocated (always allocated) Component
137
132
 
138
133
  ```ts
139
- import { type ThunkModuleToFunc, useThunk, getDefaultID, getState } from '@chhsiao1981/use-thunk'
140
- import * as DoModule from '../reducers/module'
134
+ import { type toDoModule, useThunk, getState } from '@chhsiao1981/use-thunk'
135
+ import * as DoModule from '../thunks/module'
141
136
 
142
- type TDoModule = ThunkModuleToFunc<typeof DoModule>
137
+ type doModule = toDoModule<typeof DoModule>
143
138
 
144
139
  const Component = () => {
145
- const [stateModule, doModule] = useThunk<DoModule.State, TDoModule>(DoModule)
146
-
147
- const moduleID = getDefaultID(stateModule)
148
- const theModule = getState(stateModule)
140
+ const useModule = useThunk<DoModule.State, doModule>(DoModule)
141
+ const [module, doModule, moduleID] = getState(useModule)
149
142
 
150
- .
151
- .
152
- .
143
+ .
144
+ .
145
+ .
153
146
  }
154
147
  ```
155
148
 
156
- ### Must Included in main.tsx
149
+ ### Must Included in `main.tsx`
157
150
 
158
151
  ```tsx
159
- registerThunk(...)
152
+ import { registerThunk, ThunkContext } from '@chhsiao1981/use-thunk'
153
+ import * as DoModule from '../thunks/module'
154
+ registerThunk(DoModule)
160
155
  .
161
156
  .
162
157
  .
@@ -182,8 +177,8 @@ with the following features:
182
177
  For example, the example [in the redux link](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape) is represented as:
183
178
 
184
179
  ```ts
185
- classStatePost = {
186
- myClass: 'post',
180
+ moduleStatePost = {
181
+ name: 'post',
187
182
  nodes: {
188
183
  [uuid-post1] : {
189
184
  id: uuid-post1,
@@ -202,7 +197,7 @@ classStatePost = {
202
197
  }
203
198
  }
204
199
  },
205
- defaultID,
200
+ defaultID,s
206
201
  defaultState,
207
202
  }
208
203
  ```
@@ -210,8 +205,8 @@ classStatePost = {
210
205
  and:
211
206
 
212
207
  ```ts
213
- classStateComment = {
214
- myClass: 'comment',
208
+ moduleStateComment = {
209
+ myClass: 'module',
215
210
  nodes: {
216
211
  [uuid-comment1] : {
217
212
  id: uuid-comment1,
@@ -256,8 +251,8 @@ classStateComment = {
256
251
 
257
252
  and:
258
253
  ```ts
259
- classStateUser = {
260
- myClass: 'user',
254
+ moduleStateUser = {
255
+ name: 'user',
261
256
  nodes: {
262
257
  [uuid-user1] : {
263
258
  id: uuid-user1,
@@ -288,52 +283,124 @@ classStateUser = {
288
283
 
289
284
  ## [APIs](https://github.com/chhsiao1981/use-thunk/blob/main/src/index.d.ts)
290
285
 
291
- ### Basic
286
+ #### Types
292
287
 
293
- ##### `useThunk(theDo: ThunkModuleFunc): [ClassState, DispatchedFuncMap]`
288
+ ##### `type Thunk`
294
289
 
295
- Similar to `React.useReducer`, but we use `useThunk`, and we also bind the actions with dispatch (similar concept as `mapDispatchToProps`).s
290
+ `Thunk` is defined as:
296
291
 
297
- return: `[ClassState<S>, DispatchedFuncMap<S, R>]`
292
+ ```ts
293
+ export type Thunk<S extends State> = (
294
+ set: set<S>,
295
+ get: (id?: string) => S,
296
+ getOrNull: (id?: string) => S | null | undefined,
297
+ dispatch: dispatch<S>,
298
+ getModuleState: () => ModuleState<S>,
299
+ ) => void
300
+ ```
301
+
302
+ We generally use only `set` and `get`.
303
+
304
+ ##### `ThunkModule<S extends State>`
305
+
306
+ ```ts
307
+ export type ThunkModule<S extends State> = {
308
+ name: string
309
+ defaultState: S
310
+
311
+ // The rest of the variables are doModule.
312
+ // Specifying index-signatures to include all the variables.
313
+ [action: string]: ThunkFunc<S> | string | S
314
+ }
315
+ ```
298
316
 
299
- ##### `init({myID, parentID, doParent, state}, myuuidv4?)`
317
+ ##### `doModule<S extends State>`
300
318
 
301
- initializing the react-object.
319
+ ```ts
320
+ export interface doModule<S extends State> {
321
+ [action: string]: ThunkFunc<S>
322
+ }
323
+ ```
302
324
 
303
- ##### `setData(myID, data)`
325
+ ##### `ThunkFunc<S extends State>`
304
326
 
305
- set the data to myID.
327
+ ```ts
328
+ export type ThunkFunc<S extends State> = (...params: any[]) => Thunk<S>
329
+ ```
306
330
 
307
- ##### `remove(myID, isFromParent=false)`
331
+ #### RegisterThunk / ThunkContext / useThunk
308
332
 
309
- remove the react-object.
333
+ ##### `registerThunk(module: ThunkModule)`
310
334
 
311
- ##### `genUUID(myuuidv4?: () => string): string`
335
+ register a thunk module.
312
336
 
313
- generate uuid for react-object.
337
+ ##### `<ThunkContext>{children}</ThunkContext>`
314
338
 
315
- ### State
339
+ Rendering thunk context.
316
340
 
317
- ##### `getState(state: ClassState, myID?: string): State | null`
341
+ ##### `useThunk(theDo: ThunkModule): UseThunk`
318
342
 
319
- Get the state of `myID`. Get the state of `defaultID` if `myID` is not present. Return `null` if not available.
343
+ Similar to `React.useReducer`, but we use `useThunk`, and we also bind the actions with `set` (similar concept as `mapDispatchToProps`).
320
344
 
321
- ##### `mustGetState(state: ClassState, myID?: string): State`
345
+ return: `UseThunk`
322
346
 
323
- Get the state of `myID`. Get the state of `defaultID` if `myID` is not present. Return `defaultState` if not available.
347
+ #### State
324
348
 
325
- ##### `mustGetStateByThunk(theUseThunk: UseThunk, myID?: string): [State, DispatchedActionMap, theID]`
349
+ ##### `getState(theUseThunk: UseThunk, myID?: string): [state, doModule, theID]`
326
350
 
327
351
  Get the state of `myID` by `UseThunk`. Get the state of `defaultID` if `myID` is not present. Return `defaultState` if not available.
328
352
 
329
- return: `[S, DispatchedFuncMap<S, R>, theID]`
353
+ return: `[state, doModule, theID]`
354
+ o
355
+ ### Advanced Usage
356
+
357
+ #### types
358
+
359
+ ##### `UseThunk`
360
+
361
+ ```ts
362
+ export type UseThunk<S extends State, R extends doModule<S>> = [Readonly<ModuleState<S>>, setMap<S, R>]
363
+ ```
364
+
365
+ ##### `setMap`
366
+
367
+ ```ts
368
+ export type setMap<S extends State, T extends doModule<S>> = {
369
+ [action in keyof T]: VoidReturnType<T[action]>
370
+ } & Omit<DefaultSetMap, keyof T>
371
+ ```
330
372
 
331
- ##### `getDefaultID(state: ClassState): string`
373
+ #### Default Thunk Functions.
374
+
375
+ ##### `init({myID, parentID, doParent, state})`
376
+
377
+ initialize the state.
378
+
379
+ ##### `update(myID, data)`
380
+
381
+ update the data to myID.
382
+
383
+ ##### `upsert(myID, data)`
384
+
385
+ initialize the state if it does not exist, and update the data to myID.
386
+
387
+ ##### `remove(myID)`
388
+
389
+ remove the state.
390
+
391
+ #### State
392
+
393
+ ##### `getStateOrNullByModule(moduleState: ModuleState, myID?: string): state | null`
394
+
395
+ Get the state of `myID`. Get the state of `defaultID` if `myID` is not present. Return `null` if not available.
396
+
397
+ ##### `getStateByModule(moduleState: ModuleState, myID?: string): state`
398
+
399
+ Get the state of `myID`. Get the state of `defaultID` if `myID` is not present. Return `defaultState` if not available.
332
400
 
333
- get the default id.
401
+ #### Misc
334
402
 
335
- ### NodeState
403
+ ##### `genID(): string`
336
404
 
337
- ##### `getNode(state: ClassState, myID?: string): NodeState`
405
+ generate id for the state.
338
406
 
339
- Get the node of `myID`. Get the node of `defaultID` if `myID` is not present.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chhsiao1981/use-thunk",
3
- "version": "11.0.0",
3
+ "version": "13.0.0",
4
4
  "type": "module",
5
5
  "description": "A framework easily using useThunk to manage the data-state.",
6
6
  "homepage": "https://github.com/chhsiao1981/use-thunk",
@@ -36,28 +36,22 @@
36
36
  },
37
37
  "license": "MIT",
38
38
  "devDependencies": {
39
- "@codecov/vite-plugin": "^1.9.1",
40
- "@eslint/js": "^9.39.4",
41
- "@types/node": "^25.5.0",
39
+ "@codecov/vite-plugin": "^2.0.1",
40
+ "@types/node": "^25.9.3",
42
41
  "@types/react": ">=18.3.1",
43
42
  "@types/react-dom": ">=18.3.1",
44
- "@vitejs/plugin-react-swc": "^4.3.0",
45
- "@vitest/coverage-v8": "^4.1.0",
46
- "eslint": "^9.39.4",
47
- "eslint-plugin-react-hooks": "^7.0.1",
48
- "eslint-plugin-react-refresh": "^0.5.2",
49
- "globals": "^17.4.0",
50
- "happy-dom": "^20.8.4",
51
- "jiti": "^2.6.1",
43
+ "@vitejs/plugin-react-swc": "^4.3.1",
44
+ "@vitest/coverage-v8": "^4.1.8",
45
+ "globals": "^17.6.0",
46
+ "happy-dom": "^20.10.2",
47
+ "jiti": "^2.7.0",
52
48
  "react-dom": ">=18.3.1",
53
49
  "rimraf": "^6.1.3",
54
50
  "typescript": "~5.9.3",
55
- "typescript-eslint": "^8.57.1",
56
51
  "vite": "^6.4.1",
57
- "vitest": "^4.1.0"
52
+ "vitest": "^4.1.8"
58
53
  },
59
54
  "peerDependencies": {
60
- "react": ">=18.3.1",
61
- "uuid": "^14.0.0"
55
+ "react": ">=18.3.1"
62
56
  }
63
57
  }
@@ -1,4 +1,4 @@
1
- import type { State } from '../stateTypes'
1
+ import type { State } from '../states'
2
2
  import type BaseAction from './baseAction'
3
3
  import type { Thunk } from './thunk'
4
4
 
@@ -1,4 +1,4 @@
1
- import type { ModuleState, State } from '../stateTypes'
1
+ import type { ModuleState, State } from '../states'
2
2
  import type { ActionOrThunk } from './ActionOrThunk'
3
3
  import type BaseAction from './baseAction'
4
4
  import type { Thunk } from './thunk'
@@ -12,5 +12,5 @@ export type ActionFunc<S extends State> = (...params: any[]) => ActionOrThunk<S>
12
12
  // biome-ignore lint/suspicious/noExplicitAny: params can by any type.
13
13
  export type ThunkFunc<S extends State> = (...params: any[]) => Thunk<S>
14
14
 
15
- // GetModuleState
16
- export type GetModuleState<S extends State> = () => ModuleState<S>
15
+ // getModuleState
16
+ export type getModuleState<S extends State> = () => ModuleState<S>
@@ -1,5 +1,13 @@
1
+ import type { dispatch } from '../dispatch'
2
+ import type { get, getOrNull } from '../get'
1
3
  import type { set } from '../set'
2
- import type { ModuleState, State } from '../stateTypes'
4
+ import type { ModuleState, State } from '../states'
3
5
 
4
6
  // Thunk
5
- export type Thunk<S extends State> = (set: set<S>, getModuleState: () => ModuleState<S>) => void
7
+ export type Thunk<S extends State> = (
8
+ set: set<S>,
9
+ get: get<S>,
10
+ getOrNull: getOrNull<S>,
11
+ dispatch: dispatch<S>,
12
+ getModuleState: () => ModuleState<S>,
13
+ ) => void
@@ -0,0 +1,15 @@
1
+ // default thunks will be used in thunkModule/defaultDoModule and defaultReduceMap
2
+ import { INIT, type InitParams, init, reduceInit } from './init'
3
+ export { INIT, type InitParams, init, reduceInit }
4
+
5
+ import { reduceUpdate, UPDATE, update } from './update'
6
+ export { reduceUpdate, UPDATE, update }
7
+
8
+ import { REMOVE, reduceRemove, remove } from './remove'
9
+ export { REMOVE, reduceRemove, remove }
10
+
11
+ import { reduceUpsert, UPSERT, upsert } from './upsert'
12
+ export { UPSERT, upsert, reduceUpsert }
13
+
14
+ import { reduceSetDefaultID, SET_DEFAULT_ID, setDefaultID } from './setDefaultID'
15
+ export { SET_DEFAULT_ID, setDefaultID, reduceSetDefaultID }
@@ -0,0 +1,27 @@
1
+ import type { Thunk } from '../../action'
2
+ import type { State } from '../../states'
3
+ import { genID } from '../../utils'
4
+ import { setDefaultID } from '../setDefaultID'
5
+ import initCore, { INIT, reduceInit } from './initCore'
6
+ export { INIT, reduceInit }
7
+
8
+ // InitParams
9
+ export interface InitParams<S extends State> {
10
+ myID?: string
11
+ state: S
12
+ }
13
+
14
+ export const init = <S extends State>(params: InitParams<S>): Thunk<S> => {
15
+ return (set, _get, _getOrNull, _dispatch, getModuleState) => {
16
+ const myID = params.myID ?? genID()
17
+
18
+ const { state } = params
19
+ set(initCore(myID, state))
20
+
21
+ const { defaultID } = getModuleState()
22
+
23
+ if (!defaultID) {
24
+ set(setDefaultID(myID))
25
+ }
26
+ }
27
+ }
@@ -1,5 +1,5 @@
1
- import type BaseAction from '../action/baseAction'
2
- import type { ModuleState, NodeState, NodeStateMap, State } from '../stateTypes'
1
+ import type { BaseAction } from '../../action'
2
+ import type { ModuleState, NodeState, NodeStateMap, State } from '../../states'
3
3
 
4
4
  export interface InitAction<S extends State> extends BaseAction {
5
5
  state: S
@@ -1,5 +1,5 @@
1
- import type BaseAction from './action/baseAction'
2
- import type { ModuleState, NodeStateMap, State } from './stateTypes'
1
+ import type { BaseAction } from '../action'
2
+ import type { ModuleState, NodeStateMap, State } from '../states'
3
3
 
4
4
  export const REMOVE = '@chhsiao1981/use-thunk/REMOVE'
5
5
  export const remove = (myID: string): BaseAction => ({
@@ -1,5 +1,5 @@
1
- import type BaseAction from './action/baseAction'
2
- import type { ModuleState, State } from './stateTypes'
1
+ import type { BaseAction } from '../action'
2
+ import type { ModuleState, State } from '../states'
3
3
 
4
4
  export const SET_DEFAULT_ID = '@chhsiao1981/use-thunk/SET_DEFAULT_ID'
5
5
  export const setDefaultID = (myID: string): BaseAction => ({
@@ -13,5 +13,6 @@ export const reduceSetDefaultID = <S extends State>(
13
13
  ): ModuleState<S> => {
14
14
  const { myID } = action
15
15
 
16
- return Object.assign({}, moduleState, { defaultID: myID })
16
+ const toUpdate: Partial<ModuleState<S>> = { defaultID: myID, isInitDefaultID: true }
17
+ return Object.assign({}, moduleState, toUpdate)
17
18
  }
@@ -1,5 +1,5 @@
1
- import type BaseAction from './action/baseAction'
2
- import type { ModuleState, State } from './stateTypes'
1
+ import type { BaseAction } from '../action'
2
+ import type { ModuleState, State } from '../states'
3
3
 
4
4
  export const UPDATE = '@chhsiao1981/use-thunk/UPDATE'
5
5
  export const update = <S extends State>(myID: string, data: Partial<S>): BaseAction => ({
@@ -8,11 +8,6 @@ export const update = <S extends State>(myID: string, data: Partial<S>): BaseAct
8
8
  data,
9
9
  })
10
10
 
11
- export const setData = <S extends State>(myID: string, data: Partial<S>): BaseAction => {
12
- console.warn('setData will be deprecated in the next version.')
13
- return update(myID, data)
14
- }
15
-
16
11
  export const reduceUpdate = <S extends State>(
17
12
  moduleState: ModuleState<S>,
18
13
  action: BaseAction,
@@ -0,0 +1,27 @@
1
+ import type { BaseAction } from '../action'
2
+ import type { ModuleState, NodeState, State } from '../states'
3
+ import { deepCopy } from '../utils'
4
+
5
+ export const UPSERT = '@chhsiao1981/use-thunk/UPSERT'
6
+ export const upsert = <S extends State>(myID: string, data: Partial<S>): BaseAction => ({
7
+ myID,
8
+ type: UPSERT,
9
+ data,
10
+ })
11
+
12
+ export const reduceUpsert = <S extends State>(
13
+ moduleState: ModuleState<S>,
14
+ action: BaseAction,
15
+ ): ModuleState<S> => {
16
+ const { myID, data } = action
17
+
18
+ const myNode = moduleState.nodes[myID]
19
+ const node: NodeState<S> = myNode ?? { id: myID, state: deepCopy(moduleState.defaultState) }
20
+
21
+ const newState = Object.assign({}, node.state, data)
22
+ const newNode = Object.assign({}, node, { state: newState })
23
+ const newNodes = Object.assign({}, moduleState.nodes, { [myID]: newNode })
24
+ const newModuleState = Object.assign({}, moduleState, { nodes: newNodes })
25
+
26
+ return newModuleState
27
+ }
@@ -0,0 +1,7 @@
1
+ import type { Dispatch } from 'react'
2
+ // XXX requiring to import directly from action/ActionOrThunk, or it will cause looping.
3
+ import type { ActionOrThunk } from './action'
4
+ import type { State } from './states'
5
+
6
+ // dispatch
7
+ export type dispatch<S extends State> = Dispatch<ActionOrThunk<S>>
package/src/get.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { State } from './states'
2
+
3
+ export type get<S extends State> = (id?: string) => S
4
+ export type getOrNull<S extends State> = (id?: string) => S | null | undefined