UNPKG

@tanstack/react-db

Version:

React integration for @tanstack/db

184 lines (183 loc) 7.58 kB
"use client"; import { useRef, useCallback, useSyncExternalStore } from "react"; import { normalizeLiveQueryWindowPageSize, getLiveQueryWindowInputKind, compareLiveQueryWindowDependencies, resolveLiveQueryWindowInput, createLiveQueryCollection, getLiveQueryWindowCollectionWarning, assertLiveQueryWindowManyResult, shouldPreserveLiveQueryWindowPageCount, createLiveQueryWindowController, fetchNextLiveQueryWindowPage } from "@tanstack/db"; import { useOptionalDbClient } from "./DbProvider.js"; import { warnDeprecatedDepsArray, prepareDerivedQuery, warnUnhashableDerivedIdentity, prepareQueryValue } from "./useLiveQuery.js"; const DEFAULT_GC_TIME_MS = 1; const unpreparedQueryValue = /* @__PURE__ */ Symbol(`unpreparedQueryValue`); function useLiveInfiniteQuery(queryFnOrCollection, config, deps) { const pageSize = normalizeLiveQueryWindowPageSize(config.pageSize); const initialPageParam = config.initialPageParam ?? 0; const contextDbClient = useOptionalDbClient(); const dbClient = config.client ?? contextDbClient; const inputIsCollection = getLiveQueryWindowInputKind(queryFnOrCollection) === `collection`; const committedRef = useRef(null); const committed = committedRef.current; const inputKind = inputIsCollection ? `collection` : `query`; const derivedIdentityProfilerRef = useRef({ renderCount: 0, totalMs: 0, maxMs: 0, warned: false }); const legacyUnhashableIdentityRef = useRef([ `legacy-unhashable` ]); const deferredCollections = /* @__PURE__ */ new Set(); let preparedQueryValue = unpreparedQueryValue; let identityDeps = []; let identityMode = `collection`; if (!inputIsCollection) { if (config.queryKey !== void 0) { identityMode = `queryKey`; identityDeps = config.queryKey; } else if (deps !== void 0) { identityMode = `legacyDeps`; identityDeps = deps; warnDeprecatedDepsArray(`useLiveInfiniteQuery`); } else if (committed?.identityMode === `derived` && committed.inputQuery === queryFnOrCollection && committed.client === dbClient) { identityMode = `derived`; identityDeps = committed.dependencies ?? []; } else { identityMode = `derived`; const preparation = prepareDerivedQuery( queryFnOrCollection, dbClient, derivedIdentityProfilerRef.current, deferredCollections ); preparedQueryValue = preparation.value; if (preparation.status === `hashable`) { identityDeps = preparation.identityDeps; } else { warnUnhashableDerivedIdentity(preparation.error); identityDeps = legacyUnhashableIdentityRef.current; } } } const usesLegacyDeps = !inputIsCollection && config.queryKey === void 0 && deps !== void 0; const dependencyComparison = compareLiveQueryWindowDependencies( committed?.dependencies, identityDeps ); const sameClient = committed?.client === dbClient; const dependenciesChanged = !inputIsCollection && (!sameClient || (usesLegacyDeps ? dependencyComparison.changed : !dependencyComparison.structurallyEqual)); const dependenciesStructurallyEqual = usesLegacyDeps && sameClient && dependencyComparison.structurallyEqual; const needsNewCollection = committed === null || committed.inputKind !== inputKind || inputIsCollection && committed.inputCollection !== queryFnOrCollection || dependenciesChanged; const pageShapeChanged = committed === null || committed.pageSize !== pageSize || committed.initialPageParam !== initialPageParam; const needsNewController = committed === null || needsNewCollection || pageShapeChanged; let renderState = committed; if (needsNewController) { let collection = committed?.collection; let warning = null; if (needsNewCollection) { let inputValue = queryFnOrCollection; if (!inputIsCollection) { if (preparedQueryValue === unpreparedQueryValue) { preparedQueryValue = prepareQueryValue( queryFnOrCollection, dbClient, deferredCollections ); } inputValue = () => preparedQueryValue; } const input = resolveLiveQueryWindowInput(inputValue); if (input.kind === `collection`) { collection = input.collection; } else { collection = createLiveQueryCollection({ query: input.query.limit(pageSize + 1).offset(0), // Construction happens during render. Synchronization starts only when // useSyncExternalStore commits the controller subscription. startSync: false, gcTime: DEFAULT_GC_TIME_MS }); } } if (!collection) { throw new Error(`useLiveInfiniteQuery: Failed to create a collection.`); } if (inputIsCollection) { warning = getLiveQueryWindowCollectionWarning(collection, pageSize + 1) ?? null; } else { assertLiveQueryWindowManyResult(collection); } const canPreservePageCount = shouldPreserveLiveQueryWindowPageCount({ hasPreviousController: committed !== null, previousInputKind: committed?.inputKind, inputKind, sameCollection: inputIsCollection && committed?.inputCollection === collection, dependenciesChanged, dependenciesStructurallyEqual, pageShapeChanged }); const previousPageCount = committed ? Math.max(1, committed.controller.getSnapshot().pages.length) : 1; const initialPageCount = canPreservePageCount ? previousPageCount : 1; renderState = { inputKind, inputCollection: inputIsCollection ? collection : null, inputQuery: inputIsCollection ? null : queryFnOrCollection, client: dbClient, identityMode, dependencies: inputIsCollection ? null : [...identityDeps], pageSize, initialPageParam, collection, controller: createLiveQueryWindowController(collection, { pageSize, initialPageParam, initialPageCount }), warning, warned: false, deferredCollections }; } const currentRenderState = renderState; const controller = currentRenderState.controller; const subscribe = useCallback( (onStoreChange) => { const unsubscribe = controller.subscribe(onStoreChange); committedRef.current = currentRenderState; if (currentRenderState.warning && !currentRenderState.warned) { currentRenderState.warned = true; console.warn(currentRenderState.warning); } for (const collection of currentRenderState.deferredCollections) { collection._resumeSyncStart(); } currentRenderState.deferredCollections.clear(); return unsubscribe; }, [controller, currentRenderState] ); const getSnapshot = useCallback(() => controller.getSnapshot(), [controller]); const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot); const fetchNextPage = useCallback( () => fetchNextLiveQueryWindowPage(controller), [controller] ); return { data: snapshot.data, state: snapshot.state, status: snapshot.status, isLoading: snapshot.isLoading, isReady: snapshot.isReady, isIdle: snapshot.isIdle, isError: snapshot.isError, isCleanedUp: snapshot.isCleanedUp, collection: snapshot.collection, isEnabled: snapshot.isEnabled, pages: snapshot.pages, pageParams: snapshot.pageParams, fetchNextPage, hasNextPage: snapshot.hasNextPage, isFetchingNextPage: snapshot.isFetchingNextPage, error: snapshot.error }; } export { useLiveInfiniteQuery }; //# sourceMappingURL=useLiveInfiniteQuery.js.map