floppy-disk 2.16.0 → 3.0.0-alpha.1

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 (75) hide show
  1. package/README.md +0 -835
  2. package/esm/index.d.mts +1 -0
  3. package/esm/index.mjs +1 -0
  4. package/esm/react/create-mutation.d.mts +135 -0
  5. package/esm/react/create-query.d.mts +319 -0
  6. package/esm/react/create-store.d.mts +25 -0
  7. package/esm/react/create-stores.d.mts +32 -0
  8. package/esm/react/use-isomorphic-layout-effect.d.mts +6 -0
  9. package/esm/react/use-store.d.mts +18 -0
  10. package/esm/react.d.mts +6 -0
  11. package/esm/react.mjs +503 -0
  12. package/esm/vanilla/basic.d.mts +21 -0
  13. package/esm/vanilla/hash.d.mts +7 -0
  14. package/esm/vanilla/shallow.d.mts +6 -0
  15. package/esm/vanilla/store.d.mts +75 -0
  16. package/esm/vanilla.d.mts +4 -0
  17. package/esm/vanilla.mjs +109 -0
  18. package/index.d.ts +1 -0
  19. package/index.js +12 -0
  20. package/package.json +50 -45
  21. package/react/create-mutation.d.ts +135 -0
  22. package/react/create-query.d.ts +319 -0
  23. package/react/create-store.d.ts +25 -0
  24. package/react/create-stores.d.ts +32 -0
  25. package/react/use-isomorphic-layout-effect.d.ts +6 -0
  26. package/react/use-store.d.ts +18 -0
  27. package/{lib/index.d.ts → react.d.ts} +2 -4
  28. package/react.js +511 -0
  29. package/ts_version_4.5_and_above_is_required.d.ts +0 -0
  30. package/vanilla/basic.d.ts +21 -0
  31. package/vanilla/hash.d.ts +7 -0
  32. package/vanilla/shallow.d.ts +6 -0
  33. package/vanilla/store.d.ts +75 -0
  34. package/vanilla.d.ts +4 -0
  35. package/vanilla.js +118 -0
  36. package/esm/fetcher.d.ts +0 -27
  37. package/esm/fetcher.js +0 -95
  38. package/esm/index.d.ts +0 -8
  39. package/esm/index.js +0 -8
  40. package/esm/react/create-bi-direction-query.d.ts +0 -166
  41. package/esm/react/create-bi-direction-query.js +0 -74
  42. package/esm/react/create-mutation.d.ts +0 -39
  43. package/esm/react/create-mutation.js +0 -56
  44. package/esm/react/create-query.d.ts +0 -319
  45. package/esm/react/create-query.js +0 -434
  46. package/esm/react/create-store.d.ts +0 -51
  47. package/esm/react/create-store.js +0 -38
  48. package/esm/react/create-stores.d.ts +0 -77
  49. package/esm/react/create-stores.js +0 -125
  50. package/esm/react/with-context.d.ts +0 -5
  51. package/esm/react/with-context.js +0 -14
  52. package/esm/store.d.ts +0 -24
  53. package/esm/store.js +0 -51
  54. package/esm/utils.d.ts +0 -24
  55. package/esm/utils.js +0 -31
  56. package/lib/fetcher.d.ts +0 -27
  57. package/lib/fetcher.js +0 -99
  58. package/lib/index.js +0 -11
  59. package/lib/react/create-bi-direction-query.d.ts +0 -166
  60. package/lib/react/create-bi-direction-query.js +0 -78
  61. package/lib/react/create-mutation.d.ts +0 -39
  62. package/lib/react/create-mutation.js +0 -60
  63. package/lib/react/create-query.d.ts +0 -319
  64. package/lib/react/create-query.js +0 -438
  65. package/lib/react/create-store.d.ts +0 -51
  66. package/lib/react/create-store.js +0 -42
  67. package/lib/react/create-stores.d.ts +0 -77
  68. package/lib/react/create-stores.js +0 -130
  69. package/lib/react/with-context.d.ts +0 -5
  70. package/lib/react/with-context.js +0 -18
  71. package/lib/store.d.ts +0 -24
  72. package/lib/store.js +0 -55
  73. package/lib/utils.d.ts +0 -24
  74. package/lib/utils.js +0 -39
  75. package/utils/package.json +0 -6
package/README.md CHANGED
@@ -1,838 +1,3 @@
1
1
  # Floppy Disk 💾
2
2
 
3
3
  A lightweight, simple, and powerful state management library.
4
-
5
- This library was highly-inspired by [Zustand](https://www.npmjs.com/package/zustand) and [TanStack-Query](https://tanstack.com/query).
6
- Both are awesome state manager. That's why this Floppy Disk library behaves like them, but with small DX improvement, **more power**, and **less bundle size**.
7
-
8
- **Bundle Size Comparison:**
9
-
10
- ```js
11
- import { create } from 'zustand'; // 3.3 kB (gzipped: 1.5 kB)
12
- import { createStore } from 'floppy-disk'; // 1.4 kB (gzipped: 750 B) 🎉
13
-
14
- import {
15
- QueryClient,
16
- QueryClientProvider,
17
- useQuery,
18
- useInfiniteQuery,
19
- useMutation,
20
- } from '@tanstack/react-query'; // 31.7 kB kB (gzipped: 9.2 kB)
21
- import { createQuery, createMutation } from 'floppy-disk'; // 9.7 kB (gzipped: 3.3 kB) 🎉
22
- ```
23
-
24
- - Using Zustand & React-Query: https://demo-zustand-react-query.vercel.app/
25
- 👉 Total: **309.21 kB**
26
- - Using Floppy Disk: https://demo-floppy-disk.vercel.app/
27
- 👉 Total: **272.63 kB** 🎉
28
-
29
- ## Key Features
30
-
31
- - **Create Store**
32
- - Get/set store inside/outside component
33
- - Very simple way to customize the reactivity (state update subscription)
34
- - Support middleware
35
- - Set state interception
36
- - Store event (`onSubscribe`, `onUnsubscribe`, etc.)
37
- - Use store as local state manager
38
- - **Create Stores**
39
- - Same as store, but controlled with a store key
40
- - **Create Query & Mutation**
41
- - Backend agnostic (support GraphQL & any async function)
42
- - TypeScript ready
43
- - SSR/SSG support
44
- - Custom reactivity (we choose when to re-render)
45
- - **Create query**
46
- - Dedupe multiple request
47
- - Auto-fetch on mount or manual (lazy query)
48
- - Enable/disable query
49
- - Serve stale data while revalidating
50
- - Retry on error (customizable)
51
- - Optimistic update
52
- - Invalidate query
53
- - Reset query
54
- - Query with param (query key)
55
- - Paginated/infinite query
56
- - Prefetch query
57
- - Fetch from inside/outside component
58
- - Get query state inside/outside component
59
- - Suspense mode
60
- - **Create mutation**
61
- - Mutate from inside/outside component
62
- - Get mutation state inside/outside component
63
- - ... and [a lot more](https://floppy-disk.vercel.app/)
64
-
65
- <br>
66
-
67
- ---
68
-
69
- <p align="center">
70
- View official documentation on <a href="https://floppy-disk.vercel.app/">floppy-disk.vercel.app</a>
71
- </p>
72
-
73
- ---
74
-
75
- <br>
76
-
77
- ## Table of Contents
78
-
79
- - [Key Features](#key-features)
80
- - [Table of Contents](#table-of-contents)
81
- - [Store](#store)
82
- - [Basic Concept](#basic-concept)
83
- - [Advanced Concept](#advanced-concept)
84
- - [Stores](#stores)
85
- - [Query \& Mutation](#query--mutation)
86
- - [Query State \& Network Fetching State](#query-state--network-fetching-state)
87
- - [Inherited from createStores](#inherited-from-createstores)
88
- - [Single Query](#single-query)
89
- - [Single Query with Params](#single-query-with-params)
90
- - [Paginated Query or Infinite Query](#paginated-query-or-infinite-query)
91
- - [Mutation](#mutation)
92
- - [Important Notes](#important-notes)
93
-
94
- ## Store
95
-
96
- ### Basic Concept
97
-
98
- Create a store.
99
-
100
- ```js
101
- import { createStore } from 'floppy-disk';
102
-
103
- const useCatStore = createStore(({ set }) => ({
104
- age: 0,
105
- isSleeping: false,
106
- increaseAge: () => set((state) => ({ age: state.age + 1 })),
107
- reset: () => set({ age: 0, isSleeping: false }),
108
- }));
109
- ```
110
-
111
- Use the hook anywhere, no providers are needed.
112
-
113
- ```jsx
114
- function Cat() {
115
- const age = useCatStore('age');
116
- return <div>Cat's age: {age}</div>;
117
- }
118
-
119
- function Control() {
120
- const increaseAge = useCatStore('increaseAge');
121
- return <button onClick={increaseAge}>Increase cat's age</button>;
122
- }
123
- ```
124
-
125
- > Example: [https://codesandbox.io/.../examples/react/basic](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/basic)
126
-
127
- Control the reactivity. The concept is same as useEffect dependency array.
128
-
129
- ```jsx
130
- function YourComponent() {
131
- const { age, isSleeping } = useCatStore();
132
- // Will re-render every state change ^
133
- }
134
-
135
- function YourComponent() {
136
- const { age, isSleeping } = useCatStore((state) => [state.isSleeping]);
137
- // Will only re-render when isSleeping is updated ^
138
- // Update on age won't cause re-render this component
139
- }
140
-
141
- function YourComponent() {
142
- const { age, isSleeping } = useCatStore((state) => [state.age, state.isSleeping]);
143
- // Will re-render when age or isSleeping is updated ^
144
- }
145
-
146
- function YourComponent() {
147
- const { age, isSleeping } = useCatStore((state) => [state.age > 3]);
148
- // Will only re-render when (age>3) is updated
149
- }
150
- ```
151
-
152
- Even simpler way, after version `2.13.0`, we can use store's object key:
153
-
154
- ```jsx
155
- function YourComponent() {
156
- const age = useCatStore('age');
157
- // Will only re-render when age is updated
158
- }
159
-
160
- function YourComponent() {
161
- const age = useCatStore('isSleeping');
162
- // Will only re-render when isSleeping is updated
163
- }
164
- ```
165
-
166
- > Example: [https://codesandbox.io/.../examples/react/custom-reactivity](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/custom-reactivity)
167
-
168
- Reading/writing state and reacting to changes outside of components.
169
-
170
- ```js
171
- const alertCatAge = () => {
172
- alert(useCatStore.get().age);
173
- };
174
-
175
- const toggleIsSleeping = () => {
176
- useCatStore.set((state) => ({ isSleeping: !state.isSleeping }));
177
- };
178
-
179
- const unsub = useCatStore.subscribe(
180
- // Action
181
- (state) => {
182
- console.log('The value of age is changed!', state.age);
183
- },
184
- // Reactivity dependency (just like useEffect dependency mentioned above)
185
- (state) => [state.age],
186
- // ^If not set, the action will be triggered on every state change
187
- );
188
- ```
189
-
190
- ### Advanced Concept
191
-
192
- Set the state **silently** (without broadcast the state change to **any subscribers**).
193
-
194
- ```jsx
195
- const decreaseAgeSilently = () => {
196
- useCatStore.set((state) => ({ age: state.age }), true);
197
- // ^silent param
198
- };
199
- // 👇 Will not re-render
200
- function Cat() {
201
- const age = useCatStore('age');
202
- return <div>Cat's age: {age}</div>;
203
- }
204
- ```
205
-
206
- Store events & interception.
207
-
208
- ```js
209
- const useCatStore = createStore(
210
- ({ set }) => ({
211
- age: 0,
212
- isSleeping: false,
213
- increaseAge: () => set((state) => ({ age: state.age + 1 })),
214
- reset: () => set({ age: 0, isSleeping: false }),
215
- }),
216
- {
217
- onFirstSubscribe: (state) => {
218
- console.log('onFirstSubscribe', state);
219
- },
220
- onSubscribe: (state) => {
221
- console.log('onSubscribe', state);
222
- },
223
- onUnsubscribe: (state) => {
224
- console.log('onUnsubscribe', state);
225
- },
226
- onLastUnsubscribe: (state) => {
227
- console.log('onLastUnsubscribe', state);
228
- },
229
- intercept: (nextState, prevState) => {
230
- if (nextState.age !== prevState.age) {
231
- return { ...nextState, isSleeping: false };
232
- }
233
- return nextState;
234
- },
235
- },
236
- );
237
- ```
238
-
239
- > Example:
240
- > [https://codesandbox.io/.../examples/react/store-event](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/store-event)
241
- > [https://codesandbox.io/.../examples/react/intercept](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/intercept)
242
-
243
- Let's go wild using IIFE.
244
-
245
- ```js
246
- const useCatStore = createStore(
247
- ({ set }) => ({
248
- age: 0,
249
- isSleeping: false,
250
- increaseAge: () => set((state) => ({ age: state.age + 1 })),
251
- reset: () => set({ age: 0, isSleeping: false }),
252
- }),
253
- (() => {
254
- const validateCat = () => {
255
- console.info('Window focus event triggered...');
256
- const { age } = useCatStore.get();
257
- if (age > 5) useCatStore.set({ age: 1 });
258
- };
259
- return {
260
- onFirstSubscribe: () => window.addEventListener('focus', validateCat),
261
- onLastUnsubscribe: () => window.removeEventListener('focus', validateCat),
262
- };
263
- })(),
264
- );
265
- ```
266
-
267
- Prevent re-render using `Watch`.
268
-
269
- ```jsx
270
- function CatPage() {
271
- const age = useCatStore('age');
272
- // If age changed, this component will re-render which will cause
273
- // HeavyComponent1 & HeavyComponent2 to be re-rendered as well.
274
- return (
275
- <main>
276
- <HeavyComponent1 />
277
- <div>Cat's age: {age}</div>
278
- <HeavyComponent2 />
279
- </main>
280
- );
281
- }
282
-
283
- // Optimized
284
- function CatPageOptimized() {
285
- return (
286
- <main>
287
- <HeavyComponent1 />
288
- <useCatStore.Watch
289
- selectDeps="age"
290
- render={(age) => {
291
- return <div>Cat's age: {age}</div>;
292
- }}
293
- />
294
- <HeavyComponent2 />
295
- </main>
296
- );
297
- }
298
- ```
299
-
300
- > Example: [https://codesandbox.io/.../examples/react/watch-component](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/watch-component)
301
-
302
- Want a local state instead of global state?
303
- Or, want to set the initial state inside component?
304
-
305
- ```jsx
306
- const [CatStoreProvider, useCatStoreContext] = withContext(() =>
307
- createStore(({ set }) => ({
308
- age: 0,
309
- isSleeping: false,
310
- increaseAge: () => set((state) => ({ age: state.age + 1 })),
311
- reset: () => set({ age: 0, isSleeping: false }),
312
- })),
313
- );
314
-
315
- function Parent() {
316
- return (
317
- <>
318
- <CatStoreProvider>
319
- <CatAge />
320
- <CatIsSleeping />
321
- <WillNotReRenderAsCatStateChanged />
322
- </CatStoreProvider>
323
-
324
- <CatStoreProvider>
325
- <CatAge />
326
- <CatIsSleeping />
327
- <WillNotReRenderAsCatStateChanged />
328
- </CatStoreProvider>
329
-
330
- <CatStoreProvider onInitialize={(store) => store.set({ age: 99 })}>
331
- <CatAge />
332
- <CatIsSleeping />
333
- <WillNotReRenderAsCatStateChanged />
334
- </CatStoreProvider>
335
- </>
336
- );
337
- }
338
-
339
- function CatAge() {
340
- const { age } = useCatStoreContext()((state) => [state.age]);
341
-
342
- // Shorthand after v1.13.0:
343
- // const age = useCatStoreContext()('age');
344
-
345
- return <div>Age: {age}</div>;
346
- }
347
-
348
- function CatIsSleeping() {
349
- const useCatStore = useCatStoreContext();
350
- const { isSleeping } = useCatStore((state) => [state.isSleeping]);
351
-
352
- // Shorthand after v1.13.0:
353
- // const isSleeping = useCatStore('isSleeping');
354
-
355
- return (
356
- <>
357
- <div>Is Sleeping: {String(isSleeping)}</div>
358
- <button onClick={useCatStore.get().increaseAge}>Increase cat age</button>
359
- </>
360
- );
361
- }
362
- ```
363
-
364
- > Example: [https://codesandbox.io/.../examples/react/local-state](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/local-state)
365
-
366
- Set default reactivity.
367
-
368
- ```jsx
369
- const useCatStore = createStore(
370
- ({ set }) => ({
371
- age: 0,
372
- isSleeping: false,
373
- increaseAge: () => set((state) => ({ age: state.age + 1 })),
374
- reset: () => set({ age: 0, isSleeping: false }),
375
- }),
376
- {
377
- defaultDeps: (state) => [state.age], // 👈
378
- },
379
- );
380
-
381
- function Cat() {
382
- const { age } = useCatStore();
383
- // ^will only re-render when age changed
384
- return <div>Cat's age: {age}</div>;
385
- }
386
- ```
387
-
388
- ## Stores
389
-
390
- The concept is same as [store](#store), but this can be used for multiple stores.
391
-
392
- You need to specify the store key (an object) as identifier.
393
-
394
- ```js
395
- import { createStores } from 'floppy-disk';
396
-
397
- const useCatStores = createStores(
398
- ({ set, get, key }) => ({
399
- // ^store key
400
- age: 0,
401
- isSleeping: false,
402
- increaseAge: () => set((state) => ({ age: state.age + 1 })),
403
- reset: () => set({ age: 0, isSleeping: false }),
404
- }),
405
- {
406
- onBeforeChangeKey: (nextKey, prevKey) => {
407
- console.log('Store key changed', nextKey, prevKey);
408
- },
409
- // ... same as createStore
410
- },
411
- );
412
-
413
- function CatPage() {
414
- const [catId, setCatId] = useState(1);
415
-
416
- return (
417
- <>
418
- <div>Current cat id: {catId}</div>
419
- <button onClick={() => setCatId((prev) => prev - 1)}>Prev cat</button>
420
- <button onClick={() => setCatId((prev) => prev + 1)}>Next cat</button>
421
-
422
- <Cat catId={catId} />
423
- <Control catId={catId} />
424
- </>
425
- );
426
- }
427
-
428
- function Cat({ catId }) {
429
- const { age } = useCatStores({ catId }, (state) => [state.age]);
430
- return <div>Cat's age: {age}</div>;
431
- }
432
-
433
- function Control({ catId }) {
434
- const { increaseAge } = useCatStores({ catId }, (state) => [state.increaseAge]);
435
- return <button onClick={increaseAge}>Increase cat's age</button>;
436
- }
437
- ```
438
-
439
- > Example: [https://codesandbox.io/.../examples/react/stores](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/stores)
440
-
441
- <br><br>
442
-
443
- <p align="center">
444
- — ✨ 💾 ✨ —
445
- </p>
446
- <br>
447
-
448
- ## Query & Mutation
449
-
450
- With the power of `createStores` function and a bit creativity, we can easily create a hook just like `useQuery` and `useInfiniteQuery` in [React-Query](https://tanstack.com/query) using `createQuery` function.
451
-
452
- It can dedupe multiple request, handle caching, auto-update stale data, handle retry on error, handle infinite query, and many more. With the flexibility given in `createStores`, you can extend its power according to your needs.
453
-
454
- ### Query State & Network Fetching State
455
-
456
- There are 2 types of state: query (data) state & network fetching state.
457
-
458
- `status`, `isLoading`, `isSuccess`, `isError` is a query data state.
459
- It has no relation with network fetching state. ⚠️
460
- Here is the flow of the query data state:
461
-
462
- - Initial state when there is no data fetched.
463
- `{ status: 'loading', isLoading: true, isSuccess: false, isError: false }`
464
- - After data fetching:
465
- - If success
466
- `{ status: 'success', isLoading: false, isSuccess: true, isError: false }`
467
- - If error
468
- `{ status: 'error', isLoading: false, isSuccess: false, isError: true }`
469
- - After data fetched successfully, you will **always** get this state:
470
- `{ status: 'success', isLoading: false, isSuccess: true, isError: false }`
471
- - If a refetch is fired and got error, the state would be:
472
- `{ status: 'success', isLoading: false, isSuccess: true, isError: false, isRefetchError: true }`
473
- The previouse success response will be kept.
474
-
475
- For network fetching state, we use `isWaiting`.
476
- The value will be `true` if the query is called and still waiting for the response.
477
-
478
- ### Inherited from createStores
479
-
480
- The `createQuery` function inherits functionality from the `createStores` function, allowing us to perform the same result and actions available in `createStores`.
481
-
482
- ```tsx
483
- const useMyQuery = createQuery(myQueryFn, {
484
- // 👇 Same as createStores options
485
- defaultDeps: undefined,
486
- onFirstSubscribe: (state) => console.log('onFirstSubscribe', state),
487
- onSubscribe: (state) => console.log('onSubscribe', state),
488
- onUnsubscribe: (state) => console.log('onUnsubscribe', state),
489
- onLastUnsubscribe: (state) => console.log('onLastUnsubscribe', state),
490
- onBeforeChangeKey: (nextKey, prevKey) => console.log('Store key changed', nextKey, prevKey),
491
-
492
- // ... other createQuery options
493
- });
494
- ```
495
-
496
- Custom reactivity (dependency array) also works:
497
-
498
- ```tsx
499
- function QueryLoader() {
500
- // This component doesn't care whether the query is success or error.
501
- // It just listening to network fetching state. 👇
502
- const { isWaiting } = useMyQuery((state) => [state.isWaiting]);
503
- return <div>Is network fetching? {String(isWaiting)}</div>;
504
- }
505
- ```
506
-
507
- ### Single Query
508
-
509
- ```jsx
510
- const useGitHubQuery = createQuery(async () => {
511
- const res = await fetch('https://api.github.com/repos/afiiif/floppy-disk');
512
- if (res.ok) return res.json();
513
- throw res;
514
- });
515
-
516
- function SingleQuery() {
517
- const { isLoading, data } = useGitHubQuery();
518
-
519
- if (isLoading) return <div>Loading...</div>;
520
-
521
- return (
522
- <div>
523
- <h1>{data.name}</h1>
524
- <p>{data.description}</p>
525
- <strong>⭐️ {data.stargazers_count}</strong>
526
- <strong>🍴 {data.forks_count}</strong>
527
- </div>
528
- );
529
- }
530
- ```
531
-
532
- > Example: [https://codesandbox.io/.../examples/react/query](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/query)
533
-
534
- Actions:
535
-
536
- Normally, we don't need reactivity for the actions.
537
- Therefore, using `get` method will be better, since it will not re-render the component when a query state changed.
538
-
539
- ```jsx
540
- function Actions() {
541
- const { fetch, forceFetch, reset } = useGitHubQuery.get();
542
-
543
- // Or like this:
544
- // const { isLoading, data, error, fetch, forceFetch, reset } = useGitHubQuery();
545
-
546
- return (
547
- <>
548
- <button onClick={fetch}>Call query if the query data is stale</button>
549
- <button onClick={forceFetch}>Call query</button>
550
- <button onClick={reset}>Reset query</button>
551
- </>
552
- );
553
- }
554
- ```
555
-
556
- Options:
557
-
558
- ```jsx
559
- const useGitHubQuery = createQuery(
560
- async () => {
561
- const res = await fetch('https://api.github.com/repos/afiiif/floppy-disk');
562
- if (res.ok) return res.json();
563
- throw res;
564
- },
565
- {
566
- fetchOnMount: false,
567
- enabled: () => !!useUserQuery.get().data?.user,
568
- select: (response) => response.name,
569
- staleTime: Infinity, // Never stale
570
- retry: 0, // No retry
571
- onSuccess: (response) => {},
572
- onError: (error) => {},
573
- onSettled: () => {},
574
- },
575
- );
576
-
577
- function MyComponent() {
578
- const { data, response } = useGitHubQuery();
579
- /**
580
- * Since in option we select the data like this:
581
- * select: (response) => response.name
582
- *
583
- * The return will be:
584
- * {
585
- * response: { id: 677863376, name: "floppy-disk", ... },
586
- * data: "floppy-disk",
587
- * ...
588
- * }
589
- */
590
- }
591
- ```
592
-
593
- Get data or do something outside component:
594
-
595
- ```jsx
596
- const getData = () => console.log(useGitHubQuery.get().data);
597
- const resetQuery = () => useGitHubQuery.get().reset();
598
-
599
- // Works just like createStores
600
- useMyQuery.get(/* ... */);
601
- useMyQuery.set(/* ... */);
602
- useMyQuery.subscribe(/* ... */);
603
- useMyQuery.getSubscribers(/* ... */);
604
- ```
605
-
606
- ### Single Query with Params
607
-
608
- ```jsx
609
- const usePokemonQuery = createQuery(async ({ pokemon }) => {
610
- const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemon}`);
611
- if (res.ok) return res.json();
612
- throw res;
613
- });
614
-
615
- function PokemonPage() {
616
- const [currentPokemon, setCurrentPokemon] = useState();
617
- const { isLoading, data } = usePokemonQuery({ pokemon: currentPokemon });
618
-
619
- if (isLoading) return <div>Loading...</div>;
620
-
621
- return (
622
- <div>
623
- <h1>{data.name}</h1>
624
- <div>Weight: {data.weight}</div>
625
- </div>
626
- );
627
- }
628
- ```
629
-
630
- > Example: [https://codesandbox.io/.../examples/react/query-with-param](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/query-with-param)
631
-
632
- Get data or do something outside component:
633
-
634
- ```jsx
635
- const getDitto = () => {
636
- console.log(usePokemonQuery.get({ pokemon: 'ditto' }).data);
637
- };
638
-
639
- const resetDitto = () => {
640
- usePokemonQuery.get({ pokemon: 'ditto' }).reset();
641
- };
642
-
643
- function Actions() {
644
- return (
645
- <>
646
- <button onClick={getDitto}>Get Ditto Data</button>
647
- <button onClick={resetDitto}>Reset Ditto</button>
648
- </>
649
- );
650
- }
651
- ```
652
-
653
- ### Paginated Query or Infinite Query
654
-
655
- ```jsx
656
- const usePokemonsInfQuery = createQuery(
657
- async (_, { pageParam = 0 }) => {
658
- const res = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=10&offset=${pageParam}`);
659
- if (res.ok) return res.json();
660
- throw res;
661
- },
662
- {
663
- select: (response, { data = [] }) => [...data, ...response.results],
664
- getNextPageParam: (lastPageResponse, i) => {
665
- if (i > 5) return undefined; // Return undefined means you have reached the end of the pages
666
- return i * 10;
667
- },
668
- },
669
- );
670
-
671
- function PokemonListPage() {
672
- const { data = [], fetchNextPage, hasNextPage, isWaitingNextPage } = usePokemonsInfQuery();
673
-
674
- return (
675
- <div>
676
- {data.map((pokemon) => (
677
- <div key={pokemon.name}>{pokemon.name}</div>
678
- ))}
679
- {isWaitingNextPage ? (
680
- <div>Loading more...</div>
681
- ) : (
682
- hasNextPage && <button onClick={fetchNextPage}>Load more</button>
683
- )}
684
- </div>
685
- );
686
- }
687
- ```
688
-
689
- > Example: [https://codesandbox.io/.../examples/react/infinite-query](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/infinite-query)
690
-
691
- **Note:**
692
-
693
- - The default stale time is 3 seconds.
694
- - The default error retry attempt is 1 time, and retry delay is 2 seconds.
695
- - The default reactivity of a query is:
696
- `(s) => [s.data, s.error, s.isWaitingNextPage, s.hasNextPage]`
697
- - Note that by default, subscribers don't listen to `isWaiting` state.
698
- - You can change the `defaultDeps` on `createQuery` options.
699
-
700
- ### Mutation
701
-
702
- ```jsx
703
- const useLoginMutation = createMutation(
704
- async (variables) => {
705
- const res = await axios.post('/auth/login', {
706
- email: variables.email,
707
- password: variables.password,
708
- });
709
- return res.data;
710
- },
711
- {
712
- onSuccess: (response, variables) => {
713
- console.log(`Logged in as ${variables.email}`);
714
- console.log(`Access token: ${response.data.accessToken}`);
715
- },
716
- },
717
- );
718
-
719
- function Login() {
720
- const { mutate, isWaiting } = useLoginMutation();
721
- const showToast = useToast();
722
- return (
723
- <div>
724
- <button
725
- disabled={isWaiting}
726
- onClick={() => {
727
- mutate({ email: 'foo@bar.baz', password: 's3cREt' }).then(({ response, error }) => {
728
- if (error) {
729
- showToast('Login failed');
730
- } else {
731
- showToast('Login success');
732
- }
733
- });
734
- }}
735
- >
736
- Login
737
- </button>
738
- </div>
739
- );
740
- }
741
- ```
742
-
743
- Optimistic update:
744
-
745
- ```jsx
746
- function SaveProduct() {
747
- const { mutate, isWaiting } = useEditProductMutation();
748
- const { getValues } = useFormContext();
749
-
750
- return (
751
- <button
752
- disabled={isWaiting}
753
- onClick={() => {
754
- const payload = getValues();
755
-
756
- const { revert, invalidate } = useProductQuery.optimisticUpdate({
757
- key: { id: payload.id },
758
- response: payload,
759
- });
760
-
761
- mutate(payload).then(({ response, error }) => {
762
- if (error) {
763
- revert();
764
- }
765
- invalidate();
766
- });
767
- }}
768
- >
769
- Save
770
- </button>
771
- );
772
- }
773
- ```
774
-
775
- > Example: [https://codesandbox.io/.../examples/react/mutation](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk-site/tree/main/examples/react/mutation)
776
-
777
- <br><br>
778
-
779
- <p align="center">
780
- — ✨ 💾 ✨ —
781
- </p>
782
- <br>
783
-
784
- ## Important Notes
785
-
786
- Don't mutate. (unless you use Immer JS library or something similar)
787
-
788
- ```js
789
- import { createStore } from 'floppy-disk';
790
-
791
- const useCartStore = createStore(({ set, get }) => ({
792
- products: [],
793
- addProduct: (newProduct) => {
794
- const currentProducts = get().products;
795
- product.push(newProduct); // ❌ Don't mutate
796
- set({ product });
797
- },
798
- }));
799
- ```
800
-
801
- Don't use conditional reactivity selector.
802
-
803
- ```jsx
804
- function Cat({ isSomething }) {
805
- const value = useCatStore(isSomething ? 'age' : 'isSleeping'); // ❌
806
- const { age } = useCatStore(isSomething ? (state) => [state.age] : null); // ❌
807
- const { age } = useCatStore((state) => (isSomething ? [state.age] : [state.isSleeping])); // ❌
808
- return <div>Cat's age: {age}</div>;
809
- }
810
- ```
811
-
812
- No need to memoize the reactivity selector.
813
-
814
- ```jsx
815
- function Cat() {
816
- const selectAge = useCallback((state) => [state.age], []); // ❌
817
- const { age } = useCatStore(selectAge);
818
- return <div>Cat's age: {age}</div>;
819
- }
820
- ```
821
-
822
- No need to memoize the store key / query key.
823
-
824
- ```jsx
825
- function PokemonsPage() {
826
- const queryKey = useMemo(() => ({ generation: 'ii', sort: 'asc' }), []); // ❌
827
- const { isLoading, data } = usePokemonsQuery(queryKey);
828
- return <div>...</div>;
829
- }
830
- ```
831
-
832
- <br>
833
-
834
- ---
835
-
836
- <p align="center">
837
- View official documentation on <a href="https://floppy-disk.vercel.app/">floppy-disk.vercel.app</a>
838
- </p>