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.
Files changed (2) hide show
  1. package/README.md +175 -31
  2. package/package.json +2 -6
package/README.md CHANGED
@@ -56,7 +56,7 @@ function Control() {
56
56
  );
57
57
  }
58
58
 
59
- // You can create a custom actions
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 myCoolQuery = createQuery(
220
+ const myQuery = createQuery(
221
221
  myAsyncFn,
222
222
  // { staleTime: 5000, revalidateOnFocus: false } <-- optional options
223
223
  );
224
224
 
225
- const useMyCoolQuery = myCoolQuery();
225
+ const useMyQuery = myQuery();
226
226
 
227
227
  // Use it inside your component:
228
228
 
229
229
  function MyComponent() {
230
- const query = useMyCoolQuery();
231
- if (query.state === "INITIAL") return <div>Loading...</div>;
232
- if (query.error) return <div>Error: {query.error.message}</div>;
233
- return <div>{JSON.stringify(query.data)}</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
- (value: `boolean`)
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 { getUserById, type GetUserByIdResponse } from "../utils";
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: 1 });
280
- const query = useUserQuery();
281
- if (query.state === "INITIAL") return <div>Loading...</div>;
282
- if (query.error) return <div>Error: {query.error.message}</div>;
283
- return <div>{JSON.stringify(query.data)}</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 (state === "INITIAL") return <div>Loading...</div>;
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.0-beta.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
+ }