@chhsiao1981/use-thunk 12.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 +143 -80
  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 +6 -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 -2
  12. package/src/defaultThunks/upsert.ts +27 -0
  13. package/src/dispatch.ts +7 -0
  14. package/src/get.ts +3 -2
  15. package/src/index.ts +43 -27
  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} +9 -4
  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} +6 -5
  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 +11 -10
  30. package/src/{useThunk.ts → useThunk/index.ts} +6 -8
  31. package/src/{useThunkReducer.ts → useThunk/useThunkReducer.ts} +41 -11
  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 -2
  44. package/types/defaultThunks/upsert.d.ts +5 -0
  45. package/types/dispatch.d.ts +4 -4
  46. package/types/get.d.ts +3 -2
  47. package/types/index.d.ts +11 -15
  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 -1
  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} +5 -4
  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 +7 -9
  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 -50
  73. package/src/thunkModule/defaultThunkModuleFuncMap.ts +0 -15
  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,43 +2,26 @@
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 [`12.0.0`](https://github.com/chhsiao1981/use-thunk/releases/tag/12.0.0): redefining the `getState` functions.
23
- 1. `getState` => `getStateOrNullByModule`, `mustGetState` => `getStateByModule`.
24
- 2. `mustGetStateByThunk` => `getState`.
25
- * Starting from [`11.0.0`](https://github.com/chhsiao1981/use-thunk/releases/tag/11.0.0):
26
- 1. `setData` => `update`, `registerThunk` => `createThunk`, `dispatch` => `set`, `ClassState` => `ModuleState`, and `ModuleState.myClass` => `ModuleState.name`.
27
- 2. `Thunk` is `(set, get, getModuleState) => {}`, where `get(id)` directly returns object-level state.
28
- * Starting from [`10.0.0`](https://github.com/chhsiao1981/use-thunk/releases/tag/10.0.0): The ClassState is shared globally, with `registerThunk` and `ThunkContext`.
29
- * Starting from [`9.0.0`](https://github.com/chhsiao1981/use-thunk/releases/tag/9.0.0): npm package is renamed as [@chhsiao1981/use-thunk](https://www.npmjs.com/package/%40chhsiao1981/use-thunk)
30
- * Starting from [`8.0.0`](https://github.com/chhsiao1981/use-thunk/releases/tag/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).
31
14
 
32
15
  ## Install
33
16
 
34
17
  npm install --save @chhsiao1981/use-thunk
35
18
 
36
- ## Example
19
+ ## Getting Started
37
20
 
38
- Thunk module able to do increment (reducers/increment.ts):
21
+ Thunk module able to do increment (thunks/increment.ts):
39
22
 
40
23
  ```ts
41
- import { init as _init, update, Thunk, type State as rState, genUUID } from '@chhsiao1981/use-thunk'
24
+ import { type Thunk, type State as rState, update } from '@chhsiao1981/use-thunk'
42
25
 
43
26
  export const name = 'demo/Increment'
44
27
 
@@ -50,51 +33,53 @@ export const defaultState: State = {
50
33
  count: 0
51
34
  }
52
35
 
53
- export const init = (): Thunk<State> => {
54
- const myID = genUUID()
55
- return async (set) => {
56
- set(_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 })
57
43
  }
58
44
  }
59
45
 
60
- export const increment = (myID: string): Thunk<State> => {
46
+ // or we can treat set as dispatching a base action.
47
+ export const increment2 = (myID: string): Thunk<State> => {
61
48
  return async (set, get) => {
62
49
  let me = get(myID)
63
- if(!me) {
64
- return
65
- }
66
50
  const {count} = me
67
51
 
68
- set(update(myID, { count: count + 1 }))
52
+ set(update({ count: count + 2 }))
53
+ }
54
+ }
55
+
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))
69
60
  }
70
61
  }
71
62
  ```
72
63
 
73
- App.tsx:
64
+ components/App.tsx:
74
65
 
75
66
  ```tsx
76
67
  import { type ThunkModuleToFunc, useThunk, getState } from '@chhsiao1981/use-thunk'
77
- import * as DoIncrement from './reducers/increment'
68
+ import * as DoIncrement from './thunks/increment'
78
69
 
79
- type TDoIncrement = ThunkModuleToFunc(typeof DoIncrement)
70
+ type doIncrement = toDoModule(typeof DoIncrement)
80
71
 
81
- type Props = {
82
- }
83
-
84
- export default (props: Props) => {
85
- const useIncrement = useThunk<DoIncrement.State, TDoIncrement>(DoIncrement, StateType.LOCAL)
72
+ export default () => {
73
+ const useIncrement = useThunk<DoIncrement.State, doIncrement>(DoIncrement)
86
74
  const [increment, doIncrement, incrementID] = getState(useIncrement)
87
75
 
88
- //init
89
- useEffect(() => {
90
- doIncrement.init()
91
- }, [])
92
-
93
76
  // to render
94
77
  return (
95
78
  <div>
96
79
  <p>count: {increment.count}</p>
97
- <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>
98
83
  </div>
99
84
  )
100
85
  }
@@ -102,13 +87,13 @@ export default (props: Props) => {
102
87
 
103
88
  main.tsx:
104
89
  ```tsx
105
- import { createThunk, ThunkContext } from "@chhsiao1981/use-thunk";
90
+ import { registerThunk, ThunkContext } from "@chhsiao1981/use-thunk";
106
91
  import { StrictMode } from "react";
107
92
  import { createRoot } from "react-dom/client";
108
- import * as DoIncrement from './reducers/increment'
109
- import App from "./App.tsx";
93
+ import * as DoIncrement from './thunks/increment'
94
+ import App from "./components/App";
110
95
 
111
- createThunk(DoIncrement)
96
+ registerThunk(DoIncrement)
112
97
 
113
98
  createRoot(document.getElementById("root")!).render(
114
99
  <StrictMode>
@@ -119,6 +104,11 @@ createRoot(document.getElementById("root")!).render(
119
104
  )
120
105
  ```
121
106
 
107
+ ## Introduction
108
+
109
+
110
+
111
+ ## Development Pattern
122
112
  ### Must Included in a Thunk Module
123
113
 
124
114
  ```ts
@@ -138,29 +128,30 @@ export const defaultState: State = {}
138
128
  .
139
129
  ```
140
130
 
141
- ### Must Included in a Top-level Component
131
+ ### Must Included in a Statically-allocated (always allocated) Component
142
132
 
143
133
  ```ts
144
- import { type ThunkModuleToFunc, useThunk, getState } from '@chhsiao1981/use-thunk'
145
- import * as DoModule from '../reducers/module'
134
+ import { type toDoModule, useThunk, getState } from '@chhsiao1981/use-thunk'
135
+ import * as DoModule from '../thunks/module'
146
136
 
147
- type TDoModule = ThunkModuleToFunc<typeof DoModule>
137
+ type doModule = toDoModule<typeof DoModule>
148
138
 
149
139
  const Component = () => {
150
- const useModule = useThunk<DoModule.State, TDoModule>(DoModule)
140
+ const useModule = useThunk<DoModule.State, doModule>(DoModule)
151
141
  const [module, doModule, moduleID] = getState(useModule)
152
142
 
153
143
  .
154
- .
155
- .
144
+ .
145
+ .
156
146
  }
157
147
  ```
158
148
 
159
- ### Must Included in main.tsx
149
+ ### Must Included in `main.tsx`
160
150
 
161
151
  ```tsx
162
- import { createThunk, ThunkContext } from '@chhsiao1981/use-thunk'
163
- createThunk(...)
152
+ import { registerThunk, ThunkContext } from '@chhsiao1981/use-thunk'
153
+ import * as DoModule from '../thunks/module'
154
+ registerThunk(DoModule)
164
155
  .
165
156
  .
166
157
  .
@@ -292,47 +283,112 @@ moduleStateUser = {
292
283
 
293
284
  ## [APIs](https://github.com/chhsiao1981/use-thunk/blob/main/src/index.d.ts)
294
285
 
295
- ### Basic
286
+ #### Types
296
287
 
297
- ##### `createThunk(theModule: ThunkModule)`
288
+ ##### `type Thunk`
298
289
 
299
- Create a module state for `theModule`.
290
+ `Thunk` is defined as:
300
291
 
301
- ##### `<ThunkContext>{children}</ThunkContext>`
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
+ ```
302
301
 
303
- Rendering Thunk context.
302
+ We generally use only `set` and `get`.
304
303
 
305
- ##### `useThunk(theDo: ThunkModule): UseThunk`
304
+ ##### `ThunkModule<S extends State>`
306
305
 
307
- Similar to `React.useReducer`, but we use `useThunk`, and we also bind the actions with set (similar concept as `mapDispatchToProps`).
306
+ ```ts
307
+ export type ThunkModule<S extends State> = {
308
+ name: string
309
+ defaultState: S
308
310
 
309
- return: `UseThunk`
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
+ ```
310
316
 
311
- ### Default Thunk functions.
317
+ ##### `doModule<S extends State>`
312
318
 
313
- ##### `init({myID, parentID, doParent, state}, myuuidv4?)`
319
+ ```ts
320
+ export interface doModule<S extends State> {
321
+ [action: string]: ThunkFunc<S>
322
+ }
323
+ ```
314
324
 
315
- initializing the state.
325
+ ##### `ThunkFunc<S extends State>`
316
326
 
317
- ##### `update(myID, data)`
327
+ ```ts
328
+ export type ThunkFunc<S extends State> = (...params: any[]) => Thunk<S>
329
+ ```
318
330
 
319
- update the data to myID.
331
+ #### RegisterThunk / ThunkContext / useThunk
320
332
 
321
- ##### `remove(myID)`
333
+ ##### `registerThunk(module: ThunkModule)`
322
334
 
323
- remove the state.
335
+ register a thunk module.
336
+
337
+ ##### `<ThunkContext>{children}</ThunkContext>`
324
338
 
325
- ##### `genUUID(myuuidv4?: () => string): string`
339
+ Rendering thunk context.
326
340
 
327
- generate uuid for the state.
341
+ ##### `useThunk(theDo: ThunkModule): UseThunk`
342
+
343
+ Similar to `React.useReducer`, but we use `useThunk`, and we also bind the actions with `set` (similar concept as `mapDispatchToProps`).
344
+
345
+ return: `UseThunk`
328
346
 
329
- ### State
347
+ #### State
330
348
 
331
349
  ##### `getState(theUseThunk: UseThunk, myID?: string): [state, doModule, theID]`
332
350
 
333
351
  Get the state of `myID` by `UseThunk`. Get the state of `defaultID` if `myID` is not present. Return `defaultState` if not available.
334
352
 
335
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
+ ```
372
+
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
336
392
 
337
393
  ##### `getStateOrNullByModule(moduleState: ModuleState, myID?: string): state | null`
338
394
 
@@ -341,3 +397,10 @@ Get the state of `myID`. Get the state of `defaultID` if `myID` is not present.
341
397
  ##### `getStateByModule(moduleState: ModuleState, myID?: string): state`
342
398
 
343
399
  Get the state of `myID`. Get the state of `defaultID` if `myID` is not present. Return `defaultState` if not available.
400
+
401
+ #### Misc
402
+
403
+ ##### `genID(): string`
404
+
405
+ generate id for the state.
406
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chhsiao1981/use-thunk",
3
- "version": "12.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,9 +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
7
  export type Thunk<S extends State> = (
6
8
  set: set<S>,
7
- get: (id?: string) => S | null,
9
+ get: get<S>,
10
+ getOrNull: getOrNull<S>,
11
+ dispatch: dispatch<S>,
8
12
  getModuleState: () => ModuleState<S>,
9
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 => ({
@@ -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 CHANGED
@@ -1,3 +1,4 @@
1
- import type { State } from './stateTypes'
1
+ import type { State } from './states'
2
2
 
3
- export type get = <S extends State>(id: string) => S
3
+ export type get<S extends State> = (id?: string) => S
4
+ export type getOrNull<S extends State> = (id?: string) => S | null | undefined
package/src/index.ts CHANGED
@@ -1,42 +1,58 @@
1
- import type { GetModuleState, Thunk } from './action'
2
- import createThunk from './createThunk'
3
- import { genUUID } from './genUUID'
4
- import type { get } from './get'
5
- import { type InitParams, init } from './init'
6
- import { remove } from './remove'
7
- import type { set } from './set'
8
- import { setDefaultID } from './setDefaultID'
9
- import type { setMap } from './setMap'
10
- import { getDefaultID, getNode, getState, getStateByModule, getStateOrNullByModule } from './states'
11
- import type { ModuleState, State } from './stateTypes'
12
- import ThunkContext from './ThunkContext'
13
- import type { ThunkModule, ThunkModuleToFunc } from './thunkModule'
14
- import { update } from './update'
1
+ import type { getModuleState, Thunk, ThunkFunc } from './action'
2
+ import { type InitParams, init, remove, setDefaultID, update, upsert } from './defaultThunks'
3
+ import type { dispatch } from './dispatch'
4
+ import type { get, getOrNull } from './get'
5
+ import registerThunk from './registerThunk'
6
+ import type { set, setMap } from './set'
7
+ import {
8
+ getDefaultID,
9
+ getNodeOrNull,
10
+ getState,
11
+ getStateByModule,
12
+ getStateOrNullByModule,
13
+ type ModuleState,
14
+ type State,
15
+ } from './states'
16
+ import { ThunkContext } from './thunkContext'
17
+ import type { doModule, ThunkModule, toDoModule } from './thunkModule'
15
18
  import useThunk, { type UseThunk } from './useThunk'
19
+ import { genID } from './utils'
16
20
 
17
21
  export {
18
- createThunk,
22
+ // thunk definition.
23
+ type Thunk,
24
+ type set,
25
+ type get,
26
+ type getOrNull,
27
+ type dispatch,
28
+ type getModuleState,
29
+ // registerThunk / useThunk / ThunkContext
30
+ registerThunk,
19
31
  useThunk,
20
32
  type UseThunk,
21
33
  ThunkContext,
34
+ // state related.
22
35
  type State,
36
+ getState,
23
37
  type ModuleState,
24
- type GetModuleState,
25
- type Thunk,
38
+ // module related.
26
39
  type ThunkModule,
27
- type ThunkModuleToFunc,
28
- type set,
29
- type setMap,
30
- type get,
31
- getNode,
32
- getDefaultID,
33
- getStateOrNullByModule,
34
- getStateByModule,
35
- getState,
40
+ type toDoModule,
41
+ type doModule,
42
+ type ThunkFunc,
43
+ // default thunks.
36
44
  init,
37
45
  type InitParams,
38
46
  update,
47
+ upsert,
39
48
  remove,
49
+ // genID
50
+ genID,
51
+ // advanced usage.
52
+ type setMap,
40
53
  setDefaultID,
41
- genUUID,
54
+ getNodeOrNull,
55
+ getDefaultID,
56
+ getStateOrNullByModule,
57
+ getStateByModule,
42
58
  }
@@ -0,0 +1,18 @@
1
+ import type { BaseAction } from '../action'
2
+ import type { ModuleState, State } from '../states'
3
+ import { DEFAULT_REDUCE_MAP } from './defaultReduceMap'
4
+ import type { Reducer } from './index'
5
+
6
+ export const createReducer = <S extends State>(): Reducer<S> => {
7
+ return (moduleState: ModuleState<S>, action: BaseAction): ModuleState<S> => {
8
+ if (!action) {
9
+ return moduleState
10
+ }
11
+
12
+ if (DEFAULT_REDUCE_MAP[action.type]) {
13
+ return DEFAULT_REDUCE_MAP[action.type](moduleState, action)
14
+ }
15
+
16
+ return moduleState
17
+ }
18
+ }