import { Query, QueryRows, Row } from "@evolu/common/local-first"; import { use } from "react"; import { useEvolu } from "./useEvolu.js"; import type { useQueries } from "./useQueries.js"; import { useQuerySubscription } from "./useQuerySubscription.js"; import { useIsSsr } from "./useIsSsr.js"; /** * Load and subscribe to the Query, and return an object with `rows` and `row` * properties that are automatically updated when data changes. * * Note that {@link useQuery} uses React Suspense. It means every usage of * {@link useQuery} blocks rendering until loading is completed. To avoid loading * waterfall with more queries, use {@link useQueries}. * * The `promise` option allows preloading queries before rendering, which can be * useful for complex queries that might take noticeable time even with local * data. However, this is rarely needed as local queries are typically fast. * * ### Example * * ```ts * // Get all rows. * const rows = useQuery(allTodos); * * // Get rows for a specific todo (the first row can be null). * const rows = useQuery(todoById(1)); * * // Get all rows, but without subscribing to changes. * const rows = useQuery(allTodos, { once: true }); * * // Preload a query (rarely needed). * const allTodosPromise = evolu.loadQuery(allTodos); * const rows = useQuery(allTodos, { promise: allTodosPromise }); * ``` */ export const useQuery = ( query: Query, options: Partial<{ /** Without subscribing to changes. */ readonly once: boolean; /** Reuse existing promise instead of loading so query will not suspense. */ readonly promise: Promise>; }> = {}, ): QueryRows => { const evolu = useEvolu(); const isSSR = useIsSsr(); if (isSSR) { if (!options.promise) void evolu.loadQuery(query); } else { use(options.promise ?? evolu.loadQuery(query)); } return useQuerySubscription(query, options); };