@pylonsync/react 0.3.200 → 0.3.202

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/package.json +3 -3
  2. package/src/hooks.ts +32 -4
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.200",
6
+ "version": "0.3.202",
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.200",
16
- "@pylonsync/sync": "0.3.200"
15
+ "@pylonsync/sdk": "0.3.202",
16
+ "@pylonsync/sync": "0.3.202"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "react": ">=19.0.0"
package/src/hooks.ts CHANGED
@@ -74,7 +74,15 @@ export function useQuery<T = Row>(
74
74
  entity: string,
75
75
  options?: QueryOptions
76
76
  ): UseQueryReturn<T> {
77
- const loading = useRef<boolean>(sync.store.list(entity).length === 0);
77
+ // `loading` is true only while we genuinely don't know yet — i.e.,
78
+ // the engine hasn't drained IndexedDB into the in-memory store. Once
79
+ // hydrated, an empty list is a real empty (not "still fetching"), so
80
+ // refreshes with cached data don't flash "Loading…" before the rows
81
+ // render. The engine's `isHydrated()` exposes a synchronous gate that
82
+ // flips true after `loadAllEntities()` settles.
83
+ const loading = useRef<boolean>(
84
+ !sync.isHydrated() && sync.store.list(entity).length === 0,
85
+ );
78
86
  const error = useRef<Error | null>(null);
79
87
  const optionsKey = JSON.stringify(options || {});
80
88
 
@@ -104,7 +112,16 @@ export function useQuery<T = Row>(
104
112
  if (sig !== snapshotCache.current.sig) {
105
113
  snapshotCache.current = { rows: filtered as T[], sig };
106
114
  }
107
- if (rows.length > 0 && loading.current) loading.current = false;
115
+ // Loading is false the moment hydration finishes — even when the
116
+ // disk replica is empty (a real "no rows yet" state, not "still
117
+ // fetching"). The previous gate only flipped when rows arrived,
118
+ // so an entity with zero cached rows stayed in loading=true
119
+ // forever until a server pull / WS event populated it. After:
120
+ // hydration completion fires store.notify() → getSnapshot re-runs
121
+ // → loading goes false immediately, the empty state renders.
122
+ if (loading.current && (rows.length > 0 || sync.isHydrated())) {
123
+ loading.current = false;
124
+ }
108
125
  return snapshotCache.current.rows;
109
126
  }, [sync, entity, optionsKey, options]);
110
127
 
@@ -140,7 +157,12 @@ export function useQueryOne<T = Row>(
140
157
  entity: string,
141
158
  id: string
142
159
  ): UseQueryOneReturn<T> {
143
- const loading = useRef<boolean>(sync.store.get(entity, id) === null);
160
+ // Same hydration-aware loading semantics as useQuery see the
161
+ // comment there. Without this, a refreshed page flashes "Loading…"
162
+ // for the row even when it's already in IndexedDB.
163
+ const loading = useRef<boolean>(
164
+ !sync.isHydrated() && sync.store.get(entity, id) === null,
165
+ );
144
166
  const error = useRef<Error | null>(null);
145
167
 
146
168
  const subscribe = useMemo(
@@ -165,7 +187,13 @@ export function useQueryOne<T = Row>(
165
187
  if (sig !== snapshotCache.current.sig) {
166
188
  snapshotCache.current = { row: (row as T) ?? null, sig };
167
189
  }
168
- if (row !== null && loading.current) loading.current = false;
190
+ // Mirror useQuery: loading flips false on hydration completion
191
+ // even when the row is missing (not "still fetching" — genuinely
192
+ // not present yet). Without the isHydrated() check, a missing
193
+ // row stuck loading=true forever.
194
+ if (loading.current && (row !== null || sync.isHydrated())) {
195
+ loading.current = false;
196
+ }
169
197
  return snapshotCache.current.row;
170
198
  }, [sync, entity, id]);
171
199