Replies: 3 comments 2 replies
-
isLoading: The third argument to useLiveQuery() is a default value returned while loading. With IndexedDB, loading time is normally too short to make it feasible with fallback rendering. I normally just return null from the component while loading. Examples: export function FriendListComponent() {
// When returning array, we can rely on the default and do not need to specify 3rd arg:
const friends = useLiveQuery(() => db.friends.toArray());
// A falsy result means we're still loading
if (!friends) return null; // Initial render (is loading)
return <ul>{
friends.map(friend => <li key={friend.id}>{friend.name}</li>)
}</ul>;
}
export function FriendDetailsComponent(friendId) {
const friend = useLiveQuery(
() => db.friends.get(friendId), // This query may return undefined!
[friendId], // Deps
null // Specify fallback distinguished from not-found result.
);
if (friend === null) return null; // Initial render (is loading)
if (friend === undefined) return <p>Friend not found</p>; // Not found.
return <div>
<p>Name: {friend.name}</p>
<p>Age: {friend.age}</p>
</div>;
} Suspense support: We might support a suspense based useLiveQuery in future. That would require a DBCore caching middleware that is capable of caching all responses including IDBCoreCursors - which in turn would require a virtual cursor ("CachedCursor" or how to name it) to be implemented that will behave just like the original cursor. So, it's doable but not super straightforward to implement. |
Beta Was this translation helpful? Give feedback.
-
The default return worked well for me. I used a
|
Beta Was this translation helpful? Give feedback.
-
Here's my approach for adding Suspense support to // utils/useSuspenseLiveQuery.ts
import { liveQuery } from 'dexie';
import { type Atom, useAtomValue } from 'jotai';
import { atomWithObservable } from 'jotai/utils';
import { useRef } from 'react';
// Must use a global cache because Suspense breaks the rules of hooks like
// useMemo and useRef
// https://github.com/facebook/react/issues/15137
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents
const CACHE = new Map<string, Atom<any | Promise<any>> | undefined>();
// Cache for 5 minutes, which was inspired by TanStack Query
const CACHE_ITEM_LIFESPAN_MS = 1000 * 60 * 5;
type CacheKey = string | number;
// Force at least one cache key past the `use` prefixed key to ensure that
// using useSuspenseLiveQuery is necessary vs a global atom.
type CacheKeys<T = CacheKey> = [`use${string}`, T, ...T[]];
type InitialValue<Data> = Data | (() => Data);
// A suspense-enabled version of useLiveQuery from Dexie
export default function useSuspenseLiveQuery<T>(
querier: () => Promise<T> | T,
cacheKeys: CacheKeys,
): T | undefined;
export default function useSuspenseLiveQuery<T, U extends InitialValue<T>>(
querier: () => Promise<T> | T,
cacheKeys: CacheKeys,
initialValue: U,
): T | U;
export default function useSuspenseLiveQuery<T, U extends InitialValue<T>>(
querier: () => Promise<T> | T,
cacheKeys: CacheKeys,
initialValue?: U,
): T | U {
const cacheKey = cacheKeys.join(':');
const atomRef = useRef<[string, Atom<T | Promise<T>> | undefined]>([
cacheKey,
CACHE.get(cacheKey),
]);
// Look for atom in cache
if (atomRef.current[0] !== cacheKey) {
atomRef.current = [cacheKey, CACHE.get(cacheKey)];
}
// Atom not in cache
if (!atomRef.current[1]) {
const newAtom = atomWithObservable<T>(
() => liveQuery(querier),
initialValue ? { initialValue } : undefined,
);
if (process.env.NODE_ENV === 'development') {
newAtom.debugLabel = cacheKey;
}
atomRef.current = [cacheKey, newAtom];
CACHE.set(cacheKey, newAtom);
// Remove from cache after a timeout to allow for garbage collection
setTimeout(() => {
CACHE.delete(cacheKey);
}, CACHE_ITEM_LIFESPAN_MS);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return useAtomValue(atomRef.current[1]!);
} Example Use// store/sessions.ts
export function useSession(sessionId: SessionId) {
return useSuspenseLiveQuery(
() => db.sessions.get(sessionId),
['useSession', sessionId],
);
} |
Beta Was this translation helpful? Give feedback.
-
Hi, I see there is no property like
isLoading
foruseLiveQuery
result.I wonder if it is possible to adopt
Suspense
foruseLiveQuery
.Beta Was this translation helpful? Give feedback.
All reactions