@@ -32,7 +32,7 @@ import {
3232 instantiateNoopLogger ,
3333 Logger ,
3434} from "../browser/logging.js" ;
35- import { ConvexQueryOptions } from "../browser/query_options.js" ;
35+ import type { QueryOptions } from "../browser/query_options.js" ;
3636import { LoadMoreOfPaginatedQuery } from "../browser/sync/pagination.js" ;
3737import {
3838 PaginatedQueryClient ,
@@ -537,9 +537,7 @@ export class ConvexReactClient {
537537 * an optional extendSubscriptionFor for how long to subscribe to the query.
538538 */
539539 prewarmQuery < Query extends FunctionReference < "query" > > (
540- queryOptions : ConvexQueryOptions < Query > & {
541- extendSubscriptionFor ?: number ;
542- } ,
540+ queryOptions : QueryOptions < Query > & { extendSubscriptionFor ?: number } ,
543541 ) {
544542 const extendSubscriptionFor =
545543 queryOptions . extendSubscriptionFor ?? DEFAULT_EXTEND_SUBSCRIPTION_FOR ;
@@ -801,6 +799,34 @@ export type OptionalRestArgsOrSkip<FuncRef extends FunctionReference<any>> =
801799 ? [ args ?: EmptyObject | "skip" ]
802800 : [ args : FuncRef [ "_args" ] | "skip" ] ;
803801
802+ /**
803+ * Result returned by object-form {@link useQuery}.
804+ *
805+ * @public
806+ */
807+ export type UseQueryResult < QueryResult > =
808+ | {
809+ data : QueryResult ;
810+ error : undefined ;
811+ status : "success" ;
812+ }
813+ | {
814+ data : undefined ;
815+ error : Error ;
816+ status : "error" ;
817+ }
818+ | {
819+ data : undefined ;
820+ error : undefined ;
821+ status : "pending" ;
822+ } ;
823+
824+ type UseQueryOptions < Query extends FunctionReference < "query" > > = {
825+ query : Query ;
826+ args : FunctionArgs < Query > | "skip" ;
827+ throwOnError ?: boolean ;
828+ } ;
829+
804830/**
805831 * Load a reactive query within a React component.
806832 *
@@ -847,20 +873,82 @@ export type OptionalRestArgsOrSkip<FuncRef extends FunctionReference<any>> =
847873export function useQuery < Query extends FunctionReference < "query" > > (
848874 query : Query ,
849875 ...args : OptionalRestArgsOrSkip < Query >
850- ) : Query [ "_returnType" ] | undefined {
851- const skip = args [ 0 ] === "skip" ;
852- const argsObject = args [ 0 ] === "skip" ? { } : parseArgs ( args [ 0 ] ) ;
876+ ) : Query [ "_returnType" ] | undefined ;
877+
878+ /**
879+ * Load a reactive query within a React component using an options object.
880+ *
881+ * This is an alternative form of {@link useQuery} that accepts a single
882+ * {@link UseQueryOptions} object instead of positional arguments.
883+ * Errors are returned in the result object unless `throwOnError` is set.
884+ *
885+ * @example
886+ * ```tsx
887+ * import { useQuery } from "convex/react";
888+ * import { api } from "../convex/_generated/api";
889+ *
890+ * function TaskList() {
891+ * const state = useQuery({ query: api.tasks.list, args: { completed: false } });
892+ *
893+ * if (state.status === "pending") return <div>Loading...</div>;
894+ * if (state.status === "error") return <div>Error: {state.error.message}</div>;
895+ * return state.data.map((task) => <div key={task._id}>{task.text}</div>);
896+ * }
897+ * ```
898+ *
899+ * @param options - Query options. Pass `args: "skip"` to disable the query.
900+ * @returns the current query state as a {@link UseQueryResult} object.
901+ *
902+ * @see https://docs.convex.dev/client/react#fetching-data
903+ * @public
904+ */
905+ export function useQuery < Query extends FunctionReference < "query" > > (
906+ options : UseQueryOptions < Query > ,
907+ ) : UseQueryResult < Query [ "_returnType" ] > ;
853908
854- const queryReference =
855- typeof query === "string"
856- ? makeFunctionReference < "query" , any , any > ( query )
857- : query ;
909+ export function useQuery < Query extends FunctionReference < "query" > > (
910+ queryOrOptions : Query | UseQueryOptions < Query > ,
911+ ...args : OptionalRestArgsOrSkip < Query >
912+ ) : Query [ "_returnType" ] | undefined | UseQueryResult < Query [ "_returnType" ] > {
913+ const isObjectOptions =
914+ typeof queryOrOptions === "object" &&
915+ queryOrOptions !== null &&
916+ "query" in queryOrOptions ;
917+ const throwOnError = isObjectOptions
918+ ? ( queryOrOptions . throwOnError ?? false )
919+ : true ;
920+
921+ let queryReference : Query | undefined ;
922+ let argsObject : Record < string , Value > = { } ;
923+
924+ if ( isObjectOptions ) {
925+ const query = queryOrOptions . query ;
926+ queryReference =
927+ typeof query === "string"
928+ ? ( makeFunctionReference < "query" , any , any > ( query ) as Query )
929+ : query ;
930+ if ( queryOrOptions . args !== "skip" ) {
931+ argsObject = parseArgs ( queryOrOptions . args as Record < string , Value > ) ;
932+ }
933+ } else {
934+ const query = queryOrOptions ;
935+ queryReference =
936+ typeof query === "string"
937+ ? ( makeFunctionReference < "query" , any , any > ( query ) as Query )
938+ : query ;
939+ argsObject = args [ 0 ] === "skip" ? { } : parseArgs ( args [ 0 ] as Query [ "_args" ] ) ;
940+ }
858941
859- const queryName = getFunctionName ( queryReference ) ;
942+ const queryName = queryReference
943+ ? getFunctionName ( queryReference )
944+ : undefined ;
945+ const skip =
946+ ( isObjectOptions && queryOrOptions . args === "skip" ) ||
947+ ( ! isObjectOptions && args [ 0 ] === "skip" ) ;
860948
861949 const queries = useMemo (
862950 ( ) =>
863- skip
951+ skip || ! queryReference
864952 ? ( { } as RequestForQueries )
865953 : { query : { query : queryReference , args : argsObject } } ,
866954 // Stringify args so args that are semantically the same don't trigger a
@@ -871,6 +959,34 @@ export function useQuery<Query extends FunctionReference<"query">>(
871959
872960 const results = useQueries ( queries ) ;
873961 const result = results [ "query" ] ;
962+
963+ if ( isObjectOptions ) {
964+ if ( result instanceof Error ) {
965+ if ( throwOnError ) {
966+ throw result ;
967+ }
968+ return {
969+ data : undefined ,
970+ error : result ,
971+ status : "error" ,
972+ } ;
973+ }
974+
975+ if ( result === undefined ) {
976+ return {
977+ data : undefined ,
978+ error : undefined ,
979+ status : "pending" ,
980+ } ;
981+ }
982+
983+ return {
984+ data : result ,
985+ error : undefined ,
986+ status : "success" ,
987+ } ;
988+ }
989+
874990 if ( result instanceof Error ) {
875991 throw result ;
876992 }
0 commit comments