MCPcopy Index your code
hub / github.com/TanStack/db / useLiveSuspenseQuery

Function useLiveSuspenseQuery

packages/react-db/src/useLiveSuspenseQuery.ts:155–223  ·  view source on GitHub ↗
(
  configOrQueryOrCollection: any,
  deps: Array<unknown> = [],
)

Source from the content-addressed store, hash-verified

153
154// Implementation - uses useLiveQuery internally and adds Suspense logic
155export function useLiveSuspenseQuery(
156 configOrQueryOrCollection: any,
157 deps: Array<unknown> = [],
158) {
159 const promiseRef = useRef<Promise<void> | null>(null)
160 const collectionRef = useRef<Collection<any, any, any> | null>(null)
161 const hasBeenReadyRef = useRef(false)
162
163 // Use useLiveQuery to handle collection management and reactivity
164 const result = useLiveQuery(configOrQueryOrCollection, deps)
165
166 // Reset promise and ready state when collection changes (deps changed)
167 if (collectionRef.current !== result.collection) {
168 promiseRef.current = null
169 collectionRef.current = result.collection
170 hasBeenReadyRef.current = false
171 }
172
173 // SUSPENSE LOGIC: Throw promise or error based on collection status
174 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
175 if (!result.isEnabled) {
176 // Suspense queries cannot be disabled - this matches TanStack Query's useSuspenseQuery behavior
177 throw new Error(
178 `useLiveSuspenseQuery does not support disabled queries (callback returned undefined/null). ` +
179 `The Suspense pattern requires data to always be defined (T, not T | undefined). ` +
180 `Solutions: ` +
181 `1) Use conditional rendering - don't render the component until the condition is met. ` +
182 `2) Use useLiveQuery instead, which supports disabled queries with the 'isEnabled' flag.`,
183 )
184 }
185
186 // It’s not recommended to suspend a render based on a store value returned by useSyncExternalStore.
187 // result.status is the snapshot from syncExternalStore. We read the fresh status from the collection reference instead.
188 const collectionStatus = result.collection.status
189
190 // Track when we reach ready state
191 if (collectionStatus === `ready`) {
192 hasBeenReadyRef.current = true
193 promiseRef.current = null
194 }
195
196 // Only throw errors during initial load (before first ready)
197 // After success, errors surface as stale data (matches TanStack Query behavior)
198 if (collectionStatus === `error` && !hasBeenReadyRef.current) {
199 promiseRef.current = null
200 // TODO: Once collections hold a reference to their last error object (#671),
201 // we should rethrow that actual error instead of creating a generic message
202 throw new Error(`Collection "${result.collection.id}" failed to load`)
203 }
204
205 if (collectionStatus === `loading` || collectionStatus === `idle`) {
206 // Create or reuse promise for current collection
207 if (!promiseRef.current) {
208 promiseRef.current = result.collection.preload()
209 }
210 // THROW PROMISE - React Suspense catches this (React 18+ required)
211 // Note: We don't check React version here. In React <18, this will be caught
212 // by an Error Boundary, which provides a reasonable failure mode.

Callers 1

Calls 2

useLiveQueryFunction · 0.90
preloadMethod · 0.45

Tested by

no test coverage detected