@tanstack/react-db
Version:
React integration for @tanstack/db
118 lines (117 loc) • 4.74 kB
JavaScript
"use client";
import { useRef } from "react";
import { toBoundaryError, rejectLiveQueryInput } from "@tanstack/db";
import { useLiveQueryForSuspense } from "./useLiveQuery.js";
const unscopedErrorScope = {};
const initialRenderErrors = /* @__PURE__ */ new WeakMap();
function clearInitialRenderError(collection, client) {
initialRenderErrors.get(collection)?.delete(client ?? unscopedErrorScope);
}
function rememberInitialRenderError(collection, client, error, clientQuery) {
if (collection.status === `cleaned-up`) return;
let entry = initialRenderErrors.get(collection);
if (!entry) {
entry = /* @__PURE__ */ new WeakMap();
collection.once(`status:cleaned-up`, () => {
initialRenderErrors.delete(collection);
});
initialRenderErrors.set(collection, entry);
}
entry.set(client ?? unscopedErrorScope, { error, clientQuery });
}
function useLiveSuspenseQuery(configOrQueryOrCollection) {
const promiseRef = useRef(null);
const collectionRef = useRef(null);
const hasBeenReadyRef = useRef(false);
const {
result,
client,
queryHash,
identityError,
observer,
deferredCollections
} = useLiveQueryForSuspense(configOrQueryOrCollection);
if (!result.isEnabled) {
throw new Error(
`useLiveSuspenseQuery does not support disabled queries (callback returned undefined/null). The Suspense pattern requires data to always be defined (T, not T | undefined). Solutions: 1) Use conditional rendering - don't render the component until the condition is met. 2) Use useLiveQuery instead, which supports disabled queries with the 'isEnabled' flag.`
);
}
const collection = result.collection;
try {
if (collectionRef.current !== collection) {
promiseRef.current = null;
collectionRef.current = collection;
hasBeenReadyRef.current = false;
}
const collectionStatus = collection.status;
if (result.isReady) {
hasBeenReadyRef.current = true;
promiseRef.current = null;
clearInitialRenderError(collection, client);
}
const observerError = observer.getError();
const clientQuery = client && queryHash ? client._getLiveQuery(queryHash) : void 0;
if (!hasBeenReadyRef.current && (observerError !== void 0 || clientQuery?.status === `error`)) {
promiseRef.current = null;
throw observerError === void 0 ? clientQuery?.error : observerError;
}
if (client?._isSsrStreamingEnabled() && !queryHash) {
const reason = identityError ? `${identityError.reason} at ${identityError.path}` : `the query has no stable identity`;
throw new Error(
`Cannot stream this live query during SSR because ${reason}. Provide an explicit serializable queryKey.`
);
}
const initialRenderError = initialRenderErrors.get(collection)?.get(client ?? unscopedErrorScope);
if (initialRenderError?.clientQuery !== void 0 && initialRenderError.clientQuery !== clientQuery) {
clearInitialRenderError(collection, client);
} else if (initialRenderError && !hasBeenReadyRef.current) {
promiseRef.current = null;
throw toBoundaryError(initialRenderError.error);
}
if (collectionStatus === `crashed` || collectionStatus === `error` && !hasBeenReadyRef.current) {
promiseRef.current = null;
throw toBoundaryError(collection.health.error?.cause);
}
if (!hasBeenReadyRef.current && (result.isLoading || result.isIdle || result.isLoadingError || result.isCrashed)) {
if (!promiseRef.current) {
let active = true;
const stopWatchingCleanup = collection.once(`status:cleaned-up`, () => {
active = false;
});
let preload;
try {
preload = observer.preload();
} catch (error) {
stopWatchingCleanup();
throw error;
}
const preloadClientQuery = client && queryHash ? client._getLiveQuery(queryHash) : void 0;
promiseRef.current = preload.catch((error) => {
const latestClientQuery = client && queryHash ? client._getLiveQuery(queryHash) : void 0;
if (active && latestClientQuery === preloadClientQuery)
rememberInitialRenderError(
collection,
client,
error,
preloadClientQuery
);
throw error;
}).finally(stopWatchingCleanup);
}
throw promiseRef.current;
}
return {
state: result.state,
data: result.data,
collection,
health: result.health
};
} catch (error) {
if (error instanceof Promise) throw error;
return rejectLiveQueryInput(deferredCollections, error);
}
}
export {
useLiveSuspenseQuery
};
//# sourceMappingURL=useLiveSuspenseQuery.js.map