import { type CompilableQuery } from '@powersync/common'; import { usePowerSync } from '../PowerSyncContext.js'; import { useSingleQuery } from './useSingleQuery.js'; import { useWatchedQuery } from './useWatchedQuery.js'; import { AdditionalOptions, DifferentialHookOptions, QueryResult, ReadonlyQueryResult } from './watch-types.js'; import { constructCompatibleQuery } from './watch-utils.js'; import { useAllSyncStreamsHaveSynced } from '../streams.js'; /** * A hook to access the results of a watched query. * @example * * export const Component = () => { * // The lists array here will be a new Array reference whenever a change to the * // lists table is made. * const { data: lists } = useQuery('SELECT * from lists'); * * return * {lists.map((l) => ( * {JSON.stringify(l)} * ))} * * } * * export const DiffComponent = () => { * // Providing a `rowComparator` results in the hook using an incremental query under the hood. * // An incremental query will only emit results when a change to the result set occurs. * // The internal array object references are maintained for unchanged rows. * // The returned lists array is read only when a `rowComparator` is provided. * const { data: lists } = useQuery('SELECT * from lists', [], { * rowComparator: { * keyBy: (item) => item.id, * compareBy: (item) => JSON.stringify(item) * } * }); * * return * {lists.map((l) => ( * {JSON.stringify(l)} * ))} * * } */ export function useQuery( query: string | CompilableQuery, parameters?: any[], options?: AdditionalOptions ): QueryResult; export function useQuery( query: string | CompilableQuery, parameters?: any[], options?: DifferentialHookOptions ): ReadonlyQueryResult; export function useQuery( query: string | CompilableQuery, parameters: any[] = [], options: AdditionalOptions & DifferentialHookOptions = {} ) { const powerSync = usePowerSync(); if (!powerSync) { return { ..._loadingState, isLoading: false, error: new Error('PowerSync not configured.') }; } const { parsedQuery, queryChanged } = constructCompatibleQuery(query, parameters, options); const streamsHaveSynced = useAllSyncStreamsHaveSynced(powerSync, options?.streams); const runOnce = options?.runQueryOnce == true; const single = useSingleQuery({ query: parsedQuery, powerSync, queryChanged, active: runOnce && streamsHaveSynced }); const watched = useWatchedQuery({ query: parsedQuery, powerSync, queryChanged, options: { reportFetching: options.reportFetching, // Maintains backwards compatibility with previous versions // Differentiation is opt-in by default // We emit new data for each table change by default. rowComparator: options.rowComparator }, active: !runOnce && streamsHaveSynced }); if (!streamsHaveSynced) { return { ..._loadingState }; } return (runOnce ? single : watched) ?? _loadingState; } const _loadingState = { isLoading: true, isFetching: false, data: [], error: undefined };