muya 1.1.0 → 2.0.0-beta.2

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 (92) hide show
  1. package/README.md +202 -139
  2. package/cjs/index.js +1 -1
  3. package/esm/__tests__/create-async.test.js +1 -0
  4. package/esm/__tests__/test-utils.js +1 -0
  5. package/esm/create.js +1 -1
  6. package/esm/debug/development-tools.js +1 -0
  7. package/esm/index.js +1 -1
  8. package/esm/subscriber.js +1 -0
  9. package/esm/types.js +1 -1
  10. package/esm/use.js +1 -0
  11. package/esm/utils/__tests__/context.test.js +1 -0
  12. package/esm/utils/__tests__/is.test.js +1 -0
  13. package/esm/utils/__tests__/shallow.test.js +1 -0
  14. package/esm/utils/__tests__/sub-memo.test.js +1 -0
  15. package/esm/utils/common.js +1 -0
  16. package/esm/utils/create-context.js +1 -0
  17. package/esm/utils/create-emitter.js +1 -0
  18. package/esm/utils/global-scheduler.js +1 -0
  19. package/esm/utils/is.js +1 -0
  20. package/esm/utils/scheduler.js +1 -0
  21. package/esm/utils/shallow.js +1 -0
  22. package/esm/utils/sub-memo.js +1 -0
  23. package/package.json +1 -1
  24. package/packages/core/__tests__/bench.test.tsx +266 -0
  25. package/packages/core/__tests__/create-async.test.ts +88 -0
  26. package/packages/core/__tests__/create.test.tsx +107 -0
  27. package/packages/core/__tests__/subscriber.test.tsx +89 -0
  28. package/packages/core/__tests__/test-utils.ts +40 -0
  29. package/packages/core/__tests__/use-async.test.tsx +45 -0
  30. package/packages/core/__tests__/use.test.tsx +125 -0
  31. package/packages/core/create.ts +98 -0
  32. package/packages/core/debug/development-tools.ts +37 -0
  33. package/packages/core/index.ts +4 -0
  34. package/packages/core/subscriber.ts +165 -0
  35. package/packages/core/types.ts +15 -0
  36. package/packages/core/use.ts +57 -0
  37. package/packages/core/utils/__tests__/context.test.ts +198 -0
  38. package/packages/core/utils/__tests__/is.test.ts +74 -0
  39. package/packages/core/utils/__tests__/shallow.test.ts +418 -0
  40. package/packages/core/utils/__tests__/sub-memo.test.ts +13 -0
  41. package/packages/core/utils/common.ts +48 -0
  42. package/packages/core/utils/create-context.ts +60 -0
  43. package/packages/core/utils/create-emitter.ts +55 -0
  44. package/packages/core/utils/global-scheduler.ts +75 -0
  45. package/packages/core/utils/is.ts +50 -0
  46. package/packages/core/utils/scheduler.ts +59 -0
  47. package/{src → packages/core/utils}/shallow.ts +3 -6
  48. package/packages/core/utils/sub-memo.ts +49 -0
  49. package/types/__tests__/test-utils.d.ts +20 -0
  50. package/types/create.d.ts +21 -21
  51. package/types/debug/development-tools.d.ts +10 -0
  52. package/types/index.d.ts +2 -4
  53. package/types/subscriber.d.ts +25 -0
  54. package/types/types.d.ts +9 -65
  55. package/types/use.d.ts +2 -0
  56. package/types/utils/common.d.ts +15 -0
  57. package/types/utils/create-context.d.ts +5 -0
  58. package/types/utils/create-emitter.d.ts +20 -0
  59. package/types/utils/global-scheduler.d.ts +5 -0
  60. package/types/utils/is.d.ts +13 -0
  61. package/types/utils/scheduler.d.ts +8 -0
  62. package/types/utils/sub-memo.d.ts +7 -0
  63. package/esm/common.js +0 -1
  64. package/esm/create-base-state.js +0 -1
  65. package/esm/create-emitter.js +0 -1
  66. package/esm/create-getter-state.js +0 -1
  67. package/esm/is.js +0 -1
  68. package/esm/merge.js +0 -1
  69. package/esm/select.js +0 -1
  70. package/esm/shallow.js +0 -1
  71. package/esm/use-state-value.js +0 -1
  72. package/src/common.ts +0 -28
  73. package/src/create-base-state.ts +0 -35
  74. package/src/create-emitter.ts +0 -24
  75. package/src/create-getter-state.ts +0 -19
  76. package/src/create.ts +0 -102
  77. package/src/index.ts +0 -6
  78. package/src/is.ts +0 -36
  79. package/src/merge.ts +0 -41
  80. package/src/select.ts +0 -33
  81. package/src/state.test.tsx +0 -647
  82. package/src/types.ts +0 -94
  83. package/src/use-state-value.ts +0 -29
  84. package/types/common.d.ts +0 -7
  85. package/types/create-base-state.d.ts +0 -10
  86. package/types/create-emitter.d.ts +0 -7
  87. package/types/create-getter-state.d.ts +0 -6
  88. package/types/is.d.ts +0 -10
  89. package/types/merge.d.ts +0 -2
  90. package/types/select.d.ts +0 -2
  91. package/types/use-state-value.d.ts +0 -10
  92. /package/types/{shallow.d.ts → utils/shallow.d.ts} +0 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * This is not optimal, so for now just ignore. Its just for view and compare if the state is at least similar to others
3
+ * but this tests are not consider as a real benchmark
4
+ */
5
+ /* eslint-disable unicorn/consistent-function-scoping */
6
+ /* eslint-disable no-console */
7
+
8
+ import { act, renderHook } from '@testing-library/react-hooks'
9
+ import { useStore, create as zustand } from 'zustand'
10
+ import { useEffect, useState } from 'react'
11
+ import { use } from '../use'
12
+ import { atom, useAtom } from 'jotai'
13
+ import { create } from '../create'
14
+
15
+ function renderPerfHook<T>(hook: () => T, getValue: (data: T) => number, toBe: number) {
16
+ let onResolve = (_value: number) => {}
17
+ const resolvePromise = new Promise<number>((resolve) => {
18
+ onResolve = resolve
19
+ })
20
+ const start = performance.now()
21
+ const { result, waitFor } = renderHook(() => {
22
+ const data = hook()
23
+ const count = getValue(data)
24
+ useEffect(() => {
25
+ if (count === toBe) {
26
+ const end = performance.now()
27
+ onResolve(end - start)
28
+ }
29
+ }, [count])
30
+ return data
31
+ })
32
+ return { result, waitFor, resolvePromise }
33
+ }
34
+
35
+ describe('benchmarks comparison measure', () => {
36
+ const reRendersBefore = jest.fn()
37
+
38
+ beforeEach(() => {
39
+ jest.clearAllMocks()
40
+ })
41
+ const counts = [10_000]
42
+ for (const count of counts) {
43
+ describe(`Count ${count}`, () => {
44
+ it(`should benchmark ${count} muya first run - idk slow`, async () => {
45
+ const state = create(1)
46
+ // let count = 0
47
+
48
+ const { result, resolvePromise } = renderPerfHook(
49
+ () => {
50
+ reRendersBefore()
51
+ return use(state)
52
+ },
53
+ (data) => data,
54
+ count - 1,
55
+ )
56
+
57
+ for (let index = 0; index < count; index++) {
58
+ act(() => {
59
+ state.set(index)
60
+ })
61
+ }
62
+
63
+ const time = await resolvePromise
64
+ expect(result.current).toBe(count - 1)
65
+ console.log('Time', time)
66
+ console.log('Renders', reRendersBefore.mock.calls.length)
67
+ })
68
+ it(`should benchmark jotai ${count}`, async () => {
69
+ const state = atom(1)
70
+
71
+ const { result, resolvePromise } = renderPerfHook(
72
+ () => {
73
+ reRendersBefore()
74
+ return useAtom(state)
75
+ },
76
+ (data) => data[0],
77
+ count - 1,
78
+ )
79
+
80
+ for (let index = 0; index < count; index++) {
81
+ act(() => {
82
+ result.current[1](index)
83
+ })
84
+ }
85
+
86
+ const time = await resolvePromise
87
+ expect(result.current[0]).toBe(count - 1)
88
+ console.log('Time', time)
89
+ console.log('Renders', reRendersBefore.mock.calls.length)
90
+ })
91
+ it(`should benchmark zustand ${count}`, async () => {
92
+ const state = zustand((_set) => ({ state: 1 }))
93
+ const { result, resolvePromise } = renderPerfHook(
94
+ () => {
95
+ reRendersBefore()
96
+ return useStore(state)
97
+ },
98
+ (data) => data as number,
99
+ count - 1,
100
+ )
101
+
102
+ for (let index = 0; index < count; index++) {
103
+ act(() => {
104
+ state.setState(index)
105
+ })
106
+ }
107
+
108
+ const time = await resolvePromise
109
+ expect(result.current).toBe(count - 1)
110
+ console.log('Time', time)
111
+ console.log('Renders', reRendersBefore.mock.calls.length)
112
+ })
113
+
114
+ it(`should benchmark react ${count}`, async () => {
115
+ const { result, resolvePromise } = renderPerfHook(
116
+ () => {
117
+ reRendersBefore()
118
+ return useState(1)
119
+ },
120
+ (data) => data[0],
121
+ count - 1,
122
+ )
123
+
124
+ for (let index = 0; index < count; index++) {
125
+ act(() => {
126
+ result.current[1](index)
127
+ })
128
+ }
129
+
130
+ const time = await resolvePromise
131
+ expect(result.current[0]).toBe(count - 1)
132
+ console.log('Time', time)
133
+ console.log('Renders', reRendersBefore.mock.calls.length)
134
+ })
135
+ it(`should benchmark ${count} muya`, async () => {
136
+ const state = create(1)
137
+ // let count = 0
138
+
139
+ const { result, resolvePromise } = renderPerfHook(
140
+ () => {
141
+ reRendersBefore()
142
+ return use(state)
143
+ },
144
+ (data) => data,
145
+ count - 1,
146
+ )
147
+
148
+ for (let index = 0; index < count; index++) {
149
+ act(() => {
150
+ state.set(index)
151
+ })
152
+ }
153
+
154
+ const time = await resolvePromise
155
+ expect(result.current).toBe(count - 1)
156
+ console.log('Time', time)
157
+ console.log('Renders', reRendersBefore.mock.calls.length)
158
+ })
159
+ })
160
+ }
161
+ })
162
+
163
+ describe('benchmarks comparison between others', () => {
164
+ const reRendersBefore = jest.fn()
165
+
166
+ beforeEach(() => {
167
+ jest.clearAllMocks()
168
+ })
169
+ // const count = 10000
170
+
171
+ const counts = [100]
172
+ for (const count of counts) {
173
+ describe(`Count ${count}`, () => {
174
+ it(`should benchmark jotai ${count}`, async () => {
175
+ const state = atom(1)
176
+ const start = performance.now()
177
+ const { result, waitFor } = renderHook(() => {
178
+ reRendersBefore()
179
+ return useAtom(state)
180
+ })
181
+
182
+ for (let index = 0; index < count; index++) {
183
+ act(() => {
184
+ result.current[1](index)
185
+ })
186
+ }
187
+
188
+ await waitFor(() => {
189
+ expect(result.current[0]).toBe(count - 1)
190
+ })
191
+
192
+ const end = performance.now()
193
+ console.log('Time', end - start)
194
+ console.log('Renders', reRendersBefore.mock.calls.length)
195
+ // expect(reRendersBefore).toHaveBeenCalledTimes(3)
196
+ })
197
+ it(`should benchmark zustand ${count}`, async () => {
198
+ const state = zustand((_set) => ({ state: 1 }))
199
+ const start = performance.now()
200
+ const { result, waitFor } = renderHook(() => {
201
+ reRendersBefore()
202
+ return useStore(state)
203
+ })
204
+
205
+ for (let index = 0; index < count; index++) {
206
+ act(() => {
207
+ state.setState(index)
208
+ })
209
+ }
210
+
211
+ await waitFor(() => {
212
+ expect(result.current).toBe(count - 1)
213
+ })
214
+
215
+ const end = performance.now()
216
+ console.log('Time', end - start)
217
+ console.log('Renders', reRendersBefore.mock.calls.length)
218
+ })
219
+
220
+ it(`should benchmark react ${count}`, async () => {
221
+ const start = performance.now()
222
+ const { result, waitFor } = renderHook(() => {
223
+ reRendersBefore()
224
+ return useState(1)
225
+ })
226
+
227
+ for (let index = 0; index < count; index++) {
228
+ act(() => {
229
+ result.current[1](index)
230
+ })
231
+ }
232
+
233
+ await waitFor(() => {
234
+ expect(result.current[0]).toBe(count - 1)
235
+ })
236
+
237
+ const end = performance.now()
238
+ console.log('Time', end - start)
239
+ console.log('Renders', reRendersBefore.mock.calls.length)
240
+ })
241
+ })
242
+ it(`should benchmark ${count} muya`, async () => {
243
+ const state = create(1)
244
+ const start = performance.now()
245
+ const { result, waitFor } = renderHook(() => {
246
+ reRendersBefore()
247
+ return use(state)
248
+ })
249
+
250
+ for (let index = 0; index < count; index++) {
251
+ act(() => {
252
+ state.set(index)
253
+ })
254
+ }
255
+
256
+ await waitFor(() => {
257
+ expect(result.current).toBe(count - 1)
258
+ })
259
+
260
+ const end = performance.now()
261
+ console.log('Time', end - start)
262
+ console.log('Renders', reRendersBefore.mock.calls.length)
263
+ // expect(reRendersBefore).toHaveBeenCalledTimes(2)
264
+ })
265
+ }
266
+ })
@@ -0,0 +1,88 @@
1
+ import { create } from '../create'
2
+ import { waitFor } from '@testing-library/react'
3
+ import { longPromise } from './test-utils'
4
+ import { isPromise } from '../utils/is'
5
+ import { subscriber } from '../subscriber'
6
+
7
+ describe('create', () => {
8
+ it('should subscribe to context and notified it with parameters', async () => {
9
+ const state1 = create(1)
10
+ const state2 = create(2)
11
+
12
+ function derivedNested() {
13
+ return state1() + state2()
14
+ }
15
+ async function derived(plus: number) {
16
+ return state1() + state2() + derivedNested() + plus
17
+ }
18
+
19
+ let updatesCounter = 0
20
+ const sub = subscriber(() => derived(10))
21
+ expect(isPromise(sub.emitter.getSnapshot())).toBe(true)
22
+ sub.listen(async () => {
23
+ updatesCounter++
24
+ })
25
+ expect(updatesCounter).toBe(0)
26
+ // check if there is not maximum call stack
27
+ expect(await sub()).toBe(16)
28
+
29
+ state1.set(2)
30
+ //
31
+ await waitFor(async () => {})
32
+ expect(await sub()).toBe(18)
33
+
34
+ expect(updatesCounter).toBe(4)
35
+ })
36
+
37
+ it('should async subscribe to context and notified it', async () => {
38
+ const state1 = create(1)
39
+ const state2 = create(Promise.resolve(2))
40
+
41
+ async function derivedNested() {
42
+ await longPromise()
43
+ return state1() + (await state2())
44
+ }
45
+ async function derived() {
46
+ return state1() + (await state2()) + (await derivedNested())
47
+ }
48
+
49
+ let updatesCounter = 0
50
+
51
+ const sub = subscriber(derived)
52
+
53
+ sub.listen(() => {
54
+ updatesCounter++
55
+ })
56
+
57
+ // check if there is not maximum call stack
58
+ sub()
59
+
60
+ // check if not assigned multiple times, but only once
61
+ expect(state1.emitter.getSize()).toBe(1)
62
+ expect(state2.emitter.getSize()).toBe(1)
63
+ expect(sub.emitter.getSize()).toBe(1)
64
+ state1.set(2)
65
+
66
+ await waitFor(async () => {
67
+ expect(await sub()).toBe(8)
68
+ expect(updatesCounter).toBe(5)
69
+ })
70
+
71
+ state2.set(3)
72
+
73
+ await waitFor(async () => {
74
+ expect(await sub()).toBe(10)
75
+ expect(updatesCounter).toBe(10)
76
+ })
77
+
78
+ expect(state1.emitter.getSize()).toBe(1)
79
+ expect(state2.emitter.getSize()).toBe(1)
80
+ expect(sub.emitter.getSize()).toBe(1)
81
+
82
+ sub.destroy()
83
+
84
+ expect(state1.emitter.getSize()).toBe(0)
85
+ expect(state2.emitter.getSize()).toBe(0)
86
+ expect(sub.emitter.getSize()).toBe(0)
87
+ })
88
+ })
@@ -0,0 +1,107 @@
1
+ import { create } from '../create'
2
+ import { waitFor } from '@testing-library/react'
3
+ import { subscriber } from '../subscriber'
4
+
5
+ describe('create', () => {
6
+ it('should get basic value states', async () => {
7
+ const state1 = create(1)
8
+ const state2 = create(2)
9
+ expect(state1()).toBe(1)
10
+ expect(state2()).toBe(2)
11
+
12
+ state1.set(2)
13
+ state2.set(3)
14
+
15
+ await waitFor(() => {
16
+ expect(state1()).toBe(2)
17
+ expect(state2()).toBe(3)
18
+ })
19
+ })
20
+ it('should get basic and derived value', async () => {
21
+ const state1 = create(1)
22
+ const state2 = create(2)
23
+
24
+ function derived1() {
25
+ return state1() + state2()
26
+ }
27
+
28
+ expect(state1()).toBe(1)
29
+ expect(state2()).toBe(2)
30
+ expect(derived1()).toBe(3)
31
+
32
+ state1.set(2)
33
+ state2.set(3)
34
+
35
+ await waitFor(() => {
36
+ expect(state1()).toBe(2)
37
+ expect(state2()).toBe(3)
38
+ expect(derived1()).toBe(5)
39
+ })
40
+ })
41
+ it('should subscribe to context and notified it', async () => {
42
+ const state1 = create(1)
43
+ const state2 = create(2)
44
+
45
+ function derivedNested() {
46
+ return state1() + state2()
47
+ }
48
+ function derived() {
49
+ return state1() + state2() + derivedNested()
50
+ }
51
+
52
+ let updatesCounter = 0
53
+
54
+ const sub = subscriber(derived)
55
+
56
+ sub.listen(() => {
57
+ updatesCounter++
58
+ })
59
+
60
+ // check if there is not maximum call stack
61
+ sub()
62
+ sub()
63
+ sub()
64
+
65
+ // check if not assigned multiple times, but only once
66
+ expect(state1.emitter.getSize()).toBe(1)
67
+ expect(state2.emitter.getSize()).toBe(1)
68
+ expect(sub.emitter.getSize()).toBe(1)
69
+ expect(sub.emitter.getSnapshot()).toBe(6)
70
+ state1.set(2)
71
+
72
+ await waitFor(() => {
73
+ expect(sub()).toBe(8)
74
+ expect(updatesCounter).toBe(1)
75
+ })
76
+
77
+ state2.set(3)
78
+
79
+ await waitFor(() => {
80
+ expect(sub()).toBe(10)
81
+ expect(updatesCounter).toBe(2)
82
+ })
83
+
84
+ expect(state1.emitter.getSize()).toBe(1)
85
+ expect(state2.emitter.getSize()).toBe(1)
86
+ expect(sub.emitter.getSize()).toBe(1)
87
+ expect(sub.emitter.getSnapshot()).toBe(10)
88
+
89
+ sub.destroy()
90
+
91
+ expect(state1.emitter.getSize()).toBe(0)
92
+ expect(state2.emitter.getSize()).toBe(0)
93
+ expect(sub.emitter.getSize()).toBe(0)
94
+ })
95
+ it('should subscribe and set snapshot', async () => {
96
+ const state = create(1)
97
+ const sub = subscriber(state)
98
+ sub()
99
+
100
+ expect(sub.emitter.getSnapshot()).toBe(1)
101
+
102
+ state.set(2)
103
+ await waitFor(() => {
104
+ expect(sub.emitter.getSnapshot()).toBe(2)
105
+ })
106
+ })
107
+ })
@@ -0,0 +1,89 @@
1
+ import { waitFor } from '@testing-library/react'
2
+ import { create } from '../create'
3
+ import { subscriber } from '../subscriber'
4
+ import { longPromise } from './test-utils'
5
+
6
+ describe('subscriber', () => {
7
+ it('should test subscriber and cleaning the emitters', () => {
8
+ const state1 = create(1)
9
+ const sub = subscriber(state1)
10
+ // at this point, the emitter is not subscribed yet, as it need to be called first.
11
+ expect(state1.emitter.getSize()).toBe(0)
12
+
13
+ // check if the value is correct
14
+ expect(sub()).toBe(1)
15
+
16
+ // now we can check if the value is subscribed
17
+ expect(state1.emitter.getSize()).toBe(1)
18
+ // we destroy the subscriber, meaning that the emitter should be cleaned
19
+
20
+ sub.destroy()
21
+
22
+ expect(state1.emitter.getSize()).toBe(0)
23
+
24
+ // and test re-aligning the subscriber
25
+ expect(sub()).toBe(1)
26
+ expect(state1.emitter.getSize()).toBe(1)
27
+ })
28
+ it('should test how many events are emitter via singleton state', async () => {
29
+ const state1 = create(1)
30
+ const sub = subscriber(state1)
31
+
32
+ let updateCount = 0
33
+
34
+ sub.listen(() => {
35
+ updateCount++
36
+ })
37
+ sub()
38
+ await waitFor(() => {})
39
+ // we do not received initial value as it is not changed
40
+ expect(updateCount).toBe(0)
41
+
42
+ state1.set(2)
43
+ await waitFor(() => {})
44
+ expect(updateCount).toBe(1)
45
+ })
46
+ it('should test how many events are emitter via singleton async state', async () => {
47
+ const state1 = create(longPromise())
48
+ const sub = subscriber(state1)
49
+
50
+ let updateCount = 0
51
+
52
+ sub.listen(() => {
53
+ updateCount++
54
+ })
55
+ sub()
56
+ await waitFor(() => {})
57
+ // we do not received initial value as it is not changed
58
+ expect(updateCount).toBe(0)
59
+
60
+ state1.set(2)
61
+ await waitFor(() => {})
62
+ expect(updateCount).toBe(1)
63
+ })
64
+
65
+ it('should test how many events are emitter via derived state', async () => {
66
+ const state1 = create(longPromise())
67
+
68
+ async function derived() {
69
+ // await longPromise()
70
+ return await state1()
71
+ }
72
+ const sub = subscriber(derived)
73
+
74
+ let updateCount = 0
75
+
76
+ sub.listen(() => {
77
+ updateCount++
78
+ })
79
+ await sub()
80
+ await waitFor(() => {})
81
+ expect(await sub()).toBe(0)
82
+ // // we do not received initial value as it is not changed
83
+ expect(updateCount).toBe(2)
84
+
85
+ // state1.set(2)
86
+ // await waitFor(() => {})
87
+ // expect(updateCount).toBe(1)
88
+ })
89
+ })
@@ -0,0 +1,40 @@
1
+ import { Component } from 'react'
2
+
3
+ export function longPromise(time = 200): Promise<number> {
4
+ return new Promise((resolve) => {
5
+ setTimeout(() => {
6
+ resolve(0)
7
+ }, time)
8
+ })
9
+ }
10
+
11
+ // ErrorBoundary Component
12
+ export class ErrorBoundary extends Component<
13
+ { fallback: React.ReactNode; children: React.ReactNode },
14
+ { hasError: boolean; error: Error | null }
15
+ > {
16
+ constructor(props: { fallback: React.ReactNode; children: React.ReactNode }) {
17
+ super(props)
18
+ this.state = { hasError: false, error: null }
19
+ }
20
+
21
+ static getDerivedStateFromError(error: Error) {
22
+ // Update state so the next render shows the fallback UI.
23
+ return { hasError: true, error }
24
+ }
25
+
26
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
27
+ // You can log the error to an error reporting service here
28
+ // eslint-disable-next-line no-console
29
+ console.error('ErrorBoundary caught an error:', error, errorInfo)
30
+ }
31
+
32
+ render() {
33
+ if (this.state.hasError) {
34
+ // Render fallback UI
35
+ return this.props.fallback
36
+ }
37
+
38
+ return this.props.children
39
+ }
40
+ }
@@ -0,0 +1,45 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+ import { renderHook } from '@testing-library/react-hooks'
3
+ import { create } from '../create'
4
+ import { use } from '../use'
5
+ import { Suspense } from 'react'
6
+ import { waitFor } from '@testing-library/react'
7
+
8
+ describe('use-create', () => {
9
+ const reRendersBefore = jest.fn()
10
+
11
+ beforeEach(() => {
12
+ jest.clearAllMocks()
13
+ })
14
+
15
+ it('should test sub hook', async () => {
16
+ const userState = create({ name: 'John', age: 30 })
17
+ async function getDataWithUser() {
18
+ const result = await fetch('https://jsonplaceholder.typicode.com/todos/1')
19
+ const json = await result.json()
20
+ return { age: userState().age, ...json }
21
+ }
22
+
23
+ const suspenseFunction = jest.fn()
24
+ function Loading() {
25
+ suspenseFunction()
26
+ return <div>Loading...</div>
27
+ }
28
+
29
+ const { result } = renderHook(
30
+ () => {
31
+ reRendersBefore()
32
+ const data = use(getDataWithUser)
33
+ return data
34
+ },
35
+ // @ts-expect-error
36
+ { wrapper: ({ children }) => <Suspense fallback={<Loading />}>{children}</Suspense> },
37
+ )
38
+
39
+ await waitFor(() => {
40
+ expect(result.current).toEqual({ userId: 1, id: 1, title: 'delectus aut autem', completed: false, age: 30 })
41
+ })
42
+ expect(suspenseFunction).toHaveBeenCalledTimes(1)
43
+ expect(result.current).toEqual({ userId: 1, id: 1, title: 'delectus aut autem', completed: false, age: 30 })
44
+ })
45
+ })