UNPKG

@tanstack/react-db

Version:

React integration for @tanstack/db

156 lines (155 loc) • 5.74 kB
"use client"; import { useRef, useCallback, useSyncExternalStore } from "react"; import { readLiveQueryWindowPageShape, isCollection, getLiveQueryKey, bindLiveQueryWindow, rejectLiveQueryInput, releaseDeferredOnReclaim, resumeDeferredLiveQueryCollections, fetchNextLiveQueryWindowPage, createLiveQuerySourceIdClaims } from "@tanstack/db"; import { useOptionalDbClient } from "./DbProvider.js"; import { prepareDerivedQuery, prepareQueryValue, getIdentityScope } from "./useLiveQuery.js"; const unpreparedQueryValue = /* @__PURE__ */ Symbol(`unpreparedQueryValue`); function useLiveInfiniteQuery(queryFnOrCollection, config) { const pageShape = readLiveQueryWindowPageShape(config); const contextDbClient = useOptionalDbClient(); const dbClient = config.client ?? contextDbClient; const inputIsCollection = isCollection(queryFnOrCollection); const committedRef = useRef(null); const committed = committedRef.current; const renderedRef = useRef(null); const sourceIdsRef = useRef(null); const derivedIdentityProfilerRef = useRef({ renderCount: 0, totalMs: 0, maxMs: 0, warned: false }); const deferredCollections = /* @__PURE__ */ new Set(); const identityScope = () => getIdentityScope( sourceIdsRef.current ??= createLiveQuerySourceIdClaims(), dbClient, `useLiveInfiniteQuery` ); const queryKey = getLiveQueryKey(config); let preparedQueryValue = unpreparedQueryValue; let dependencies; let derivedQuery = void 0; const rendered = renderedRef.current; let binding; try { if (!inputIsCollection) { if (queryKey !== void 0) { dependencies = queryKey; } else if (committed !== null && committed.derivedQuery === queryFnOrCollection && committed.binding.scope === dbClient) { derivedQuery = queryFnOrCollection; dependencies = committed.binding.dependencies; } else { const preparation = prepareDerivedQuery( queryFnOrCollection, derivedIdentityProfilerRef.current, identityScope(), deferredCollections ); preparedQueryValue = preparation.value; derivedQuery = queryFnOrCollection; dependencies = preparation.identityDeps; } } const compileQuery = () => { if (preparedQueryValue === unpreparedQueryValue) { preparedQueryValue = prepareQueryValue( queryFnOrCollection, dbClient, deferredCollections ); } return preparedQueryValue; }; binding = bindLiveQueryWindow( committed?.binding ?? null, { ...pageShape, input: typeof queryFnOrCollection === `function` ? compileQuery : queryFnOrCollection, dependencies, scope: dbClient, client: dbClient, queryKey, recompile: `identity` }, rendered?.binding ); } catch (error) { rejectLiveQueryInput(deferredCollections, error); } let currentRenderState = committed; if (binding !== committed?.binding) { const newCollection = binding.collection !== committed?.binding.collection; if (newCollection && binding.collection === rendered?.binding.collection) { for (const deferred of rendered.deferredCollections) { deferredCollections.add(deferred); } } const reusesRendered = newCollection && binding.collection === rendered?.binding.collection; currentRenderState = { binding, derivedQuery, deferredCollections, // A render that never commits releases its deferred sync starts when // garbage collection reclaims the window collection it built. A reused // collection keeps the release its first render registered. cancelReclaimRelease: reusesRendered ? rendered.cancelReclaimRelease : newCollection && binding.ownsCollection ? releaseDeferredOnReclaim(binding.collection, deferredCollections) : void 0, warned: false }; if (binding.supplied) renderedRef.current = null; else if (newCollection && binding.ownsCollection) { renderedRef.current = currentRenderState; } } const renderState = currentRenderState; const controller = renderState.binding.controller; const subscribe = useCallback( (onStoreChange) => { const unsubscribe = controller.subscribe(onStoreChange); committedRef.current = renderState; if (renderState.binding.warning && !renderState.warned) { renderState.warned = true; console.warn(renderState.binding.warning); } renderState.cancelReclaimRelease?.(); resumeDeferredLiveQueryCollections(renderState.deferredCollections); return unsubscribe; }, [controller, renderState] ); const getSnapshot = useCallback(() => controller.getSnapshot(), [controller]); const snapshot = useSyncExternalStore( subscribe, getSnapshot, () => controller.getServerSnapshot() ); const fetchNextPage = useCallback( () => fetchNextLiveQueryWindowPage(controller), [controller] ); return { data: snapshot.data, get state() { return snapshot.state; }, status: snapshot.status, isLoading: snapshot.isLoading, isReady: snapshot.isReady, isIdle: snapshot.isIdle, isLoadingError: snapshot.isLoadingError, isCrashed: snapshot.isCrashed, isCleanedUp: snapshot.isCleanedUp, isSyncError: snapshot.isSyncError, collection: snapshot.collection, isEnabled: snapshot.isEnabled, pages: snapshot.pages, pageParams: snapshot.pageParams, fetchNextPage, hasNextPage: snapshot.hasNextPage, isFetchingNextPage: snapshot.isFetchingNextPage, health: snapshot.health }; } export { useLiveInfiniteQuery }; //# sourceMappingURL=useLiveInfiniteQuery.js.map