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 };