@pylonsync/react 0.3.265 → 0.3.266
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/package.json +3 -3
- package/src/hooks.ts +27 -24
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.3.
|
|
6
|
+
"version": "0.3.266",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "src/index.ts",
|
|
9
9
|
"types": "src/index.ts",
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"check": "tsc -p tsconfig.json --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@pylonsync/sdk": "0.3.
|
|
16
|
-
"@pylonsync/sync": "0.3.
|
|
15
|
+
"@pylonsync/sdk": "0.3.266",
|
|
16
|
+
"@pylonsync/sync": "0.3.266"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"react": ">=19.0.0"
|
package/src/hooks.ts
CHANGED
|
@@ -74,14 +74,17 @@ export function useQuery<T = Row>(
|
|
|
74
74
|
entity: string,
|
|
75
75
|
options?: QueryOptions
|
|
76
76
|
): UseQueryReturn<T> {
|
|
77
|
-
// `loading` is true only while we genuinely don't know yet — i.e.,
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
77
|
+
// `loading` is true only while we genuinely don't know yet — i.e., the
|
|
78
|
+
// engine doesn't yet have a SERVER-confirmed view. It gates on
|
|
79
|
+
// `isInitialSyncSettled()`, NOT `isHydrated()`: the latter flips true the
|
|
80
|
+
// instant IndexedDB loads, which on a cold/empty cache (first visit, or
|
|
81
|
+
// right after an org switch wipes the replica) is immediate and EMPTY — so
|
|
82
|
+
// gating on it drops `loading` while the rows are still en route from the
|
|
83
|
+
// server, flashing the empty state for the seconds until the snapshot lands.
|
|
84
|
+
// `isInitialSyncSettled()` stays pending until the first pull settles (or
|
|
85
|
+
// the cache already had rows), so callers render a skeleton instead.
|
|
83
86
|
const loading = useRef<boolean>(
|
|
84
|
-
!sync.
|
|
87
|
+
!sync.isInitialSyncSettled() && sync.store.list(entity).length === 0,
|
|
85
88
|
);
|
|
86
89
|
const error = useRef<Error | null>(null);
|
|
87
90
|
const optionsKey = JSON.stringify(options || {});
|
|
@@ -112,14 +115,13 @@ export function useQuery<T = Row>(
|
|
|
112
115
|
if (sig !== snapshotCache.current.sig) {
|
|
113
116
|
snapshotCache.current = { rows: filtered as T[], sig };
|
|
114
117
|
}
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
if (loading.current && (rows.length > 0 || sync.isHydrated())) {
|
|
118
|
+
// Drop loading when rows arrive OR the initial sync settles (first server
|
|
119
|
+
// pull done / cache had rows / fallback deadline). Gating on the settled
|
|
120
|
+
// signal — not bare hydration — is what stops the empty-state flash: a
|
|
121
|
+
// cold cache keeps loading=true until the pull confirms, then an empty
|
|
122
|
+
// result is a real "no rows" and the empty state renders. No flash, and
|
|
123
|
+
// (thanks to the fallback) no infinite spinner either.
|
|
124
|
+
if (loading.current && (rows.length > 0 || sync.isInitialSyncSettled())) {
|
|
123
125
|
loading.current = false;
|
|
124
126
|
}
|
|
125
127
|
return snapshotCache.current.rows;
|
|
@@ -167,11 +169,12 @@ export function useQueryOne<T = Row>(
|
|
|
167
169
|
entity: string,
|
|
168
170
|
id: string
|
|
169
171
|
): UseQueryOneReturn<T> {
|
|
170
|
-
// Same
|
|
171
|
-
//
|
|
172
|
-
//
|
|
172
|
+
// Same initial-sync-aware loading semantics as useQuery — see the comment
|
|
173
|
+
// there. Gates on isInitialSyncSettled() (server-confirmed) so a cold load
|
|
174
|
+
// shows a skeleton rather than flashing "not found" before the pull lands,
|
|
175
|
+
// and a refreshed page doesn't flash "Loading…" when the row is cached.
|
|
173
176
|
const loading = useRef<boolean>(
|
|
174
|
-
!sync.
|
|
177
|
+
!sync.isInitialSyncSettled() && sync.store.get(entity, id) === null,
|
|
175
178
|
);
|
|
176
179
|
const error = useRef<Error | null>(null);
|
|
177
180
|
|
|
@@ -197,11 +200,11 @@ export function useQueryOne<T = Row>(
|
|
|
197
200
|
if (sig !== snapshotCache.current.sig) {
|
|
198
201
|
snapshotCache.current = { row: (row as T) ?? null, sig };
|
|
199
202
|
}
|
|
200
|
-
// Mirror useQuery: loading flips false
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
// row
|
|
204
|
-
if (loading.current && (row !== null || sync.
|
|
203
|
+
// Mirror useQuery: loading flips false once the row arrives OR the initial
|
|
204
|
+
// sync settles (server-confirmed). Gating on isInitialSyncSettled() — not
|
|
205
|
+
// bare hydration — keeps a cold load in the skeleton state until the pull
|
|
206
|
+
// confirms, so a row that exists server-side doesn't flash "not found".
|
|
207
|
+
if (loading.current && (row !== null || sync.isInitialSyncSettled())) {
|
|
205
208
|
loading.current = false;
|
|
206
209
|
}
|
|
207
210
|
return snapshotCache.current.row;
|