floppy-disk 3.2.0-beta.2 → 3.2.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.
- package/README.md +175 -31
- package/package.json +2 -6
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ function Control() {
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
//
|
|
59
|
+
// Create your own custom action
|
|
60
60
|
const evolve = () => {
|
|
61
61
|
const { level } = useDigimon.getState();
|
|
62
62
|
|
|
@@ -84,7 +84,7 @@ const unsubscribe = useMyStore.subscribe((state, prev) => {
|
|
|
84
84
|
unsubscribe();
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
FloppyDisk provides lifecycle hooks tied to subscription count
|
|
87
|
+
FloppyDisk also provides lifecycle hooks tied to subscription count:
|
|
88
88
|
|
|
89
89
|
```tsx
|
|
90
90
|
const useTowerDefense = createStore(
|
|
@@ -217,20 +217,22 @@ Create a query using `createQuery`:
|
|
|
217
217
|
```tsx
|
|
218
218
|
import { createQuery } from "floppy-disk/react";
|
|
219
219
|
|
|
220
|
-
const
|
|
220
|
+
const myQuery = createQuery(
|
|
221
221
|
myAsyncFn,
|
|
222
222
|
// { staleTime: 5000, revalidateOnFocus: false } <-- optional options
|
|
223
223
|
);
|
|
224
224
|
|
|
225
|
-
const
|
|
225
|
+
const useMyQuery = myQuery();
|
|
226
226
|
|
|
227
227
|
// Use it inside your component:
|
|
228
228
|
|
|
229
229
|
function MyComponent() {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
return <div>{
|
|
230
|
+
const { data, error } = useMyQuery();
|
|
231
|
+
|
|
232
|
+
if (!data && !error) return <div>Loading...</div>;
|
|
233
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
234
|
+
|
|
235
|
+
return <div>{data.foo} {data.bar}</div>;
|
|
234
236
|
}
|
|
235
237
|
```
|
|
236
238
|
|
|
@@ -238,31 +240,19 @@ function MyComponent() {
|
|
|
238
240
|
|
|
239
241
|
FloppyDisk tracks two things separately:
|
|
240
242
|
|
|
241
|
-
- Is it running? → `isPending
|
|
242
|
-
|
|
243
|
-
- What's the result? → `state`\
|
|
244
|
-
(value: `INITIAL | 'SUCCESS' | 'ERROR' | 'SUCCESS_BUT_REVALIDATION_ERROR'`)
|
|
243
|
+
- Is it running? → `isPending`
|
|
244
|
+
- What's the result? → `state`
|
|
245
245
|
|
|
246
246
|
They are **independent**.
|
|
247
247
|
|
|
248
|
-
### Automatic Re-render Optimization
|
|
249
|
-
|
|
250
|
-
Just like the global store, FloppyDisk tracks usage automatically:
|
|
251
|
-
|
|
252
|
-
```tsx
|
|
253
|
-
const { data } = useMyQuery();
|
|
254
|
-
// ^Only data changes will trigger a re-render
|
|
255
|
-
|
|
256
|
-
const value = useMyQuery().data?.foo.bar.baz;
|
|
257
|
-
// ^Only data.foo.bar.baz changes will trigger a re-render
|
|
258
|
-
```
|
|
259
|
-
|
|
260
248
|
### Keyed Query (Dynamic Params)
|
|
261
249
|
|
|
262
250
|
You can create parameterized queries:
|
|
263
251
|
|
|
264
252
|
```tsx
|
|
265
|
-
import {
|
|
253
|
+
import { createQuery } from "floppy-disk/react";
|
|
254
|
+
|
|
255
|
+
import { getUserById, type GetUserByIdResponse } from "../utils"; // Your own module
|
|
266
256
|
|
|
267
257
|
type MyQueryParam = { id: string };
|
|
268
258
|
|
|
@@ -276,16 +266,42 @@ Use it with parameters:
|
|
|
276
266
|
|
|
277
267
|
```tsx
|
|
278
268
|
function UserDetail({ id }) {
|
|
279
|
-
const useUserQuery = userQuery({ id
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
return <div>{
|
|
269
|
+
const useUserQuery = userQuery({ id });
|
|
270
|
+
const { data, error } = useUserQuery();
|
|
271
|
+
|
|
272
|
+
if (!data && !error) return <div>Loading...</div>;
|
|
273
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
274
|
+
|
|
275
|
+
return <div>Name: {data.name}, email: {data.email}</div>;
|
|
284
276
|
}
|
|
285
277
|
```
|
|
286
278
|
|
|
287
279
|
Each unique parameter creates its own cache entry.
|
|
288
280
|
|
|
281
|
+
### Store Inheritance
|
|
282
|
+
|
|
283
|
+
Queries in FloppyDisk are built on top of the core store.
|
|
284
|
+
This means every query inherits the same capabilities, such as `subscribe`, `getState`, and store events.
|
|
285
|
+
It also gets **automatic reactivity** out of the box, so components rerender only when the state they use actually changes.
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
const { data } = useMyQuery();
|
|
289
|
+
// ^Only data changes will trigger a re-render
|
|
290
|
+
|
|
291
|
+
const value = useMyQuery().data?.foo.bar.baz;
|
|
292
|
+
// ^Only data.foo.bar.baz changes will trigger a re-render
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Get state outside React:
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
const myQuery = createQuery<AsyncResponse>(myAsyncFn); // Query without paramerer
|
|
299
|
+
const userQuery = createQuery<UserDetail, { id: string }>(getUserById); // Parameterized query
|
|
300
|
+
|
|
301
|
+
const getMyQueryData = () => myQuery().getState().data;
|
|
302
|
+
const getUserQueryData = ({ id }) => userQuery({ id }).getState().data;
|
|
303
|
+
```
|
|
304
|
+
|
|
289
305
|
### Infinite Query
|
|
290
306
|
|
|
291
307
|
FloppyDisk does **not provide** a dedicated "infinite query" API.\
|
|
@@ -326,7 +342,7 @@ function Page({ cursor }: { cursor?: string }) {
|
|
|
326
342
|
const usePostsQuery = postsQuery({ cursor });
|
|
327
343
|
const { state, data, error } = usePostsQuery();
|
|
328
344
|
|
|
329
|
-
if (
|
|
345
|
+
if (!data && !error) return <div>Loading...</div>;
|
|
330
346
|
if (error) return <div>Error</div>;
|
|
331
347
|
|
|
332
348
|
return (
|
|
@@ -404,3 +420,131 @@ function MyClientComponent({ initialData }) {
|
|
|
404
420
|
return <>count is {data.count}</>; // Output: count is 3
|
|
405
421
|
}
|
|
406
422
|
```
|
|
423
|
+
|
|
424
|
+
## Query State Machine
|
|
425
|
+
|
|
426
|
+
This is how the query state transition flow looks like:
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
initial failed, won't retry
|
|
430
|
+
┌────────────────────────────┐ ┌────────────────────────────┐
|
|
431
|
+
│ isPending: false │ Δ│ isPending: false │
|
|
432
|
+
│ isRevalidating: false │ │ isRevalidating: false │
|
|
433
|
+
│ │ ┌──────────────────▶ │ │
|
|
434
|
+
│ state: "INITIAL" │ │ Δ│ state: "ERROR" │
|
|
435
|
+
│ isSuccess: false │ │ │ isSuccess: false │
|
|
436
|
+
│ data: undefined │ │ │ data: undefined │
|
|
437
|
+
│ dataUpdatedAt: undefined │ │ │ dataUpdatedAt: undefined │
|
|
438
|
+
│ dataStaleAt: undefined │ │ │ dataStaleAt: undefined │
|
|
439
|
+
│ isError: false │ │ Δ│ isError: true │
|
|
440
|
+
│ error: undefined │ │ Δ│ error: <TError> │
|
|
441
|
+
│ errorUpdatedAt: undefined │ │ Δ│ errorUpdatedAt: <number> │
|
|
442
|
+
│ │ │ │ │
|
|
443
|
+
│ willRetryAt: undefined │ │ │ willRetryAt: undefined │
|
|
444
|
+
│ isRetrying: false │ │ •│ isRetrying: false │
|
|
445
|
+
│ retryCount: 0 │ │ •│ retryCount: 0 (reset) │
|
|
446
|
+
└─────────────┬──────────────┘ │ └────────────────────────────┘
|
|
447
|
+
│ │
|
|
448
|
+
│ execute │
|
|
449
|
+
▼ │ waiting retry delay
|
|
450
|
+
┌────────────────────────────┐ (N) ┌────────────────────────────┐
|
|
451
|
+
Δ│ isPending: true [ƒ]│ │ Δ│ isPending: false │
|
|
452
|
+
│ isRevalidating: false │ fail │ │ isRevalidating: false │
|
|
453
|
+
│ ├──────────▶ Should retry? ────(Y)────▶ │ │
|
|
454
|
+
│ state: "INITIAL" │ ▲ │ state: "INITIAL" │
|
|
455
|
+
│ isSuccess: false │ │ │ isSuccess: false │
|
|
456
|
+
│ data: undefined │ │ │ data: undefined │
|
|
457
|
+
│ dataUpdatedAt: undefined │ │ │ dataUpdatedAt: undefined │
|
|
458
|
+
│ dataStaleAt: undefined │ │ │ dataStaleAt: undefined │
|
|
459
|
+
│ isError: false │ │ │ isError: false │
|
|
460
|
+
│ error: undefined │ │ │ error: undefined │
|
|
461
|
+
│ errorUpdatedAt: undefined │ │ │ errorUpdatedAt: undefined │
|
|
462
|
+
│ │ │ │ │
|
|
463
|
+
│ willRetryAt: undefined │ │ Δ│ willRetryAt: <number> │
|
|
464
|
+
│ isRetrying: false │ │ │ isRetrying: false │
|
|
465
|
+
│ retryCount: 0 │ │ │ retryCount: <number> │
|
|
466
|
+
└─────────────┬──────────────┘ │ └─────────────┬──────────────┘
|
|
467
|
+
│ │ │
|
|
468
|
+
│ success │ │ retrying
|
|
469
|
+
▼ │ ▼
|
|
470
|
+
┌────────────────────────────┐ │ ┌────────────────────────────┐
|
|
471
|
+
Δ│ isPending: false │ │ Δ│ isPending: true [ƒ]│
|
|
472
|
+
│ isRevalidating: false │ │ fail │ isRevalidating: false │
|
|
473
|
+
│ │ └────────────────────┤ │
|
|
474
|
+
Δ│ state: "SUCCESS" │ │ state: "INITIAL" │
|
|
475
|
+
Δ│ isSuccess: true │ │ isSuccess: false │
|
|
476
|
+
Δ│ data: <TData> │ │ data: undefined │
|
|
477
|
+
Δ│ dataUpdatedAt: <number> │ │ dataUpdatedAt: undefined │
|
|
478
|
+
Δ│ dataStaleAt: <number> │ │ dataStaleAt: undefined │
|
|
479
|
+
│ isError: false │ │ isError: false │
|
|
480
|
+
│ error: undefined │ │ error: undefined │
|
|
481
|
+
│ errorUpdatedAt: undefined │ success │ errorUpdatedAt: undefined │
|
|
482
|
+
│ │ ◀─────────────────────────────────────┤ │
|
|
483
|
+
│ willRetryAt: undefined │ Δ│ willRetryAt: undefined │
|
|
484
|
+
•│ isRetrying: false │ Δ│ isRetrying: true │
|
|
485
|
+
•│ retryCount: 0 (reset) │ Δ│ retryCount: <number> (+1) │
|
|
486
|
+
└────────────────────────────┘ └────────────────────────────┘
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
And then after success:
|
|
490
|
+
|
|
491
|
+
```
|
|
492
|
+
success failed, won't retry
|
|
493
|
+
┌────────────────────────────┐ ┌─────────────────────────────────────────┐
|
|
494
|
+
│ isPending: false │ Δ│ isPending: false │
|
|
495
|
+
│ isRevalidating: false │ Δ│ isRevalidating: false │
|
|
496
|
+
│ │ ┌──────────────────▶ │ │
|
|
497
|
+
│ state: "SUCCESS" │ │ Δ│ state: "SUCCESS_BUT_REVALIDATION_ERROR" │
|
|
498
|
+
│ isSuccess: true │ │ │ isSuccess: true │
|
|
499
|
+
│ data: <TData> │ │ │ data: <TData> │
|
|
500
|
+
│ dataUpdatedAt: <number> │ │ │ dataUpdatedAt: <number> │
|
|
501
|
+
│ dataStaleAt: <number> │ │ │ dataStaleAt: <number> │
|
|
502
|
+
│ isError: false │ │ │ isError: false │
|
|
503
|
+
│ error: undefined │ │ Δ│ error: <TError> │
|
|
504
|
+
│ errorUpdatedAt: undefined │ │ Δ│ errorUpdatedAt: <number> │
|
|
505
|
+
│ │ │ │ │
|
|
506
|
+
│ willRetryAt: undefined │ │ │ willRetryAt: undefined │
|
|
507
|
+
│ isRetrying: false │ │ •│ isRetrying: false │
|
|
508
|
+
│ retryCount: 0 │ │ •│ retryCount: 0 (reset) │
|
|
509
|
+
└─────────────┬──────────────┘ │ └─────────────────────────────────────────┘
|
|
510
|
+
│ │
|
|
511
|
+
│ revalidate │
|
|
512
|
+
▼ │ waiting retry delay
|
|
513
|
+
┌────────────────────────────┐ (N) ┌────────────────────────────┐
|
|
514
|
+
Δ│ isPending: true [ƒ]│ │ Δ│ isPending: false │
|
|
515
|
+
Δ│ isRevalidating: true │ fail │ Δ│ isRevalidating: false │
|
|
516
|
+
│ ├──────────▶ Should retry? ────(Y)────▶ │ │
|
|
517
|
+
│ state: "SUCCESS" │ ▲ │ state: "SUCCESS" │
|
|
518
|
+
│ isSuccess: true │ │ │ isSuccess: true │
|
|
519
|
+
│ data: <TData> │ │ │ data: <TData> │
|
|
520
|
+
│ dataUpdatedAt: <number> │ │ │ dataUpdatedAt: <number> │
|
|
521
|
+
│ dataStaleAt: <number> │ │ │ dataStaleAt: <number> │
|
|
522
|
+
│ isError: false │ │ │ isError: false │
|
|
523
|
+
│ error: undefined │ │ │ error: undefined │
|
|
524
|
+
│ errorUpdatedAt: undefined │ │ │ errorUpdatedAt: undefined │
|
|
525
|
+
│ │ │ │ │
|
|
526
|
+
│ willRetryAt: undefined │ │ Δ│ willRetryAt: <number> │
|
|
527
|
+
│ isRetrying: false │ │ │ isRetrying: false │
|
|
528
|
+
│ retryCount: 0 │ │ │ retryCount: <number> │
|
|
529
|
+
└─────────────┬──────────────┘ │ └─────────────┬──────────────┘
|
|
530
|
+
│ │ │
|
|
531
|
+
│ success │ │ retrying
|
|
532
|
+
▼ │ ▼
|
|
533
|
+
┌────────────────────────────┐ │ ┌────────────────────────────┐
|
|
534
|
+
Δ│ isPending: false │ │ Δ│ isPending: true [ƒ]│
|
|
535
|
+
Δ│ isRevalidating: false │ │ fail Δ│ isRevalidating: true │
|
|
536
|
+
│ │ └────────────────────┤ │
|
|
537
|
+
│ state: "SUCCESS" │ │ state: "SUCCESS" │
|
|
538
|
+
│ isSuccess: true │ │ isSuccess: true │
|
|
539
|
+
Δ│ data: <TData> │ │ data: <TData> │
|
|
540
|
+
Δ│ dataUpdatedAt: <number> │ │ dataUpdatedAt: <number> │
|
|
541
|
+
Δ│ dataStaleAt: <number> │ │ dataStaleAt: <number> │
|
|
542
|
+
│ isError: false │ │ isError: false │
|
|
543
|
+
│ error: undefined │ │ error: undefined │
|
|
544
|
+
│ errorUpdatedAt: undefined │ success │ errorUpdatedAt: undefined │
|
|
545
|
+
│ │ ◀─────────────────────────────────────┤ │
|
|
546
|
+
│ willRetryAt: undefined │ Δ│ willRetryAt: undefined │
|
|
547
|
+
•│ isRetrying: false │ Δ│ isRetrying: true │
|
|
548
|
+
•│ retryCount: 0 (reset) │ Δ│ retryCount: <number> (+1) │
|
|
549
|
+
└────────────────────────────┘ └────────────────────────────┘
|
|
550
|
+
```
|
package/package.json
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
"name": "floppy-disk",
|
|
3
3
|
"description": "Lightweight unified state management for sync and async data.",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "3.2.
|
|
6
|
-
"publishConfig": {
|
|
7
|
-
"tag": "beta"
|
|
8
|
-
},
|
|
5
|
+
"version": "3.2.1",
|
|
9
6
|
"keywords": [
|
|
10
7
|
"utilities",
|
|
11
8
|
"store",
|
|
@@ -71,7 +68,6 @@
|
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
},
|
|
74
|
-
"packageManager": "pnpm@10.32.1",
|
|
75
71
|
"peerDependencies": {
|
|
76
72
|
"@types/react": ">=17.0",
|
|
77
73
|
"react": ">=17.0"
|
|
@@ -84,4 +80,4 @@
|
|
|
84
80
|
"optional": true
|
|
85
81
|
}
|
|
86
82
|
}
|
|
87
|
-
}
|
|
83
|
+
}
|