From b6377e0c00eb973f89978700917676322c63a91c Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 10:37:40 -0800 Subject: [PATCH 1/8] useQuery via object --- npm-packages/convex/src/react/client.ts | 213 +++++++++++++++++- npm-packages/convex/src/react/hydration.tsx | 21 +- npm-packages/convex/src/react/index.ts | 3 + .../convex/src/react/preloaded_utils.ts | 29 +++ 4 files changed, 241 insertions(+), 25 deletions(-) create mode 100644 npm-packages/convex/src/react/preloaded_utils.ts diff --git a/npm-packages/convex/src/react/client.ts b/npm-packages/convex/src/react/client.ts index 38e2be56a..c7e998053 100644 --- a/npm-packages/convex/src/react/client.ts +++ b/npm-packages/convex/src/react/client.ts @@ -38,6 +38,8 @@ import { PaginatedQueryClient, ExtendedTransition, } from "../browser/sync/paginated_query_client.js"; +import type { Preloaded } from "./hydration.js"; +import { parsePreloaded } from "./preloaded_utils.js"; // When no arguments are passed, extend subscriptions (for APIs that do this by default) // for this amount after the subscription would otherwise be dropped. @@ -801,6 +803,95 @@ export type OptionalRestArgsOrSkip> = ? [args?: EmptyObject | "skip"] : [args: FuncRef["_args"] | "skip"]; +/** + * Options for the object-based {@link useQuery} overload. + * + * @public + */ +export type UseQueryOptions> = { + /** + * The query function to run. + */ + query: Query; + /** + * Whether to throw an error if the query fails. + * If false, the error will be returned in the `error` field. + * @defaultValue false + */ + throwOnError?: boolean; + /** + * An initial value to use before the query result is available. + * @defaultValue undefined + */ + initialValue?: Query["_returnType"]; + /** + * When true, the query will not be subscribed and it will behave the same as + * if it was loading. + * @defaultValue false + */ + skip?: boolean; +} & (FunctionArgs extends EmptyObject + ? { + /** + * The arguments to the query function. + * Optional for queries with no arguments. + */ + args?: FunctionArgs; + } + : { + /** + * The arguments to the query function. + */ + args: FunctionArgs; + }); + +/** + * Options for the object-based {@link useQuery} overload with a preloaded query. + * + * @public + */ +export type UseQueryPreloadedOptions> = + { + /** + * A preloaded query result from a Server Component. + */ + preloaded: Preloaded; + /** + * Whether to throw an error if the query fails. + * If false, the error will be returned in the `error` field. + * @defaultValue false + */ + throwOnError?: boolean; + /** + * When true, the query will not be subscribed and it will behave the same as + * if it was loading. + * @defaultValue false + */ + skip?: boolean; + }; + +/** + * Result type for the object-based {@link useQuery} overload. + * + * @public + */ +export type UseQueryResult = + | { + status: "success"; + value: T; + error: undefined; + } + | { + status: "error"; + value: undefined; + error: Error; + } + | { + status: "loading"; + value: undefined; + error: undefined; + }; + /** * Load a reactive query within a React component. * @@ -820,20 +911,84 @@ export type OptionalRestArgsOrSkip> = export function useQuery>( query: Query, ...args: OptionalRestArgsOrSkip -): Query["_returnType"] | undefined { - const skip = args[0] === "skip"; - const argsObject = args[0] === "skip" ? {} : parseArgs(args[0]); +): Query["_returnType"] | undefined; - const queryReference = - typeof query === "string" - ? makeFunctionReference<"query", any, any>(query) - : query; +/** + * Load a reactive query within a React component using an options object. + * + * This overload returns an object with `status`, `error`, and `value` fields + * instead of throwing errors or returning undefined. + * + * This React hook contains internal state that will cause a rerender + * whenever the query result changes. + * + * Throws an error if not used under {@link ConvexProvider}. + * + * @param options - An options object or the string "skip" to skip the query. + * @returns An object with `status`, `error`, and `value` fields. + * + * @public + */ +export function useQuery>( + options: UseQueryOptions | UseQueryPreloadedOptions | "skip", +): UseQueryResult; + +export function useQuery>( + queryOrOptions: + | Query + | UseQueryOptions + | UseQueryPreloadedOptions + | "skip", + ...args: OptionalRestArgsOrSkip +): Query["_returnType"] | undefined | UseQueryResult { + const isObjectOptions = + typeof queryOrOptions === "object" && + queryOrOptions !== null && + ("query" in queryOrOptions || "preloaded" in queryOrOptions); + const isObjectSkip = + queryOrOptions === "skip" || (isObjectOptions && !!queryOrOptions.skip); + const isLegacy = !isObjectOptions && !isObjectSkip; + const legacySkip = isLegacy && args[0] === "skip"; + const isObjectReturn = isObjectOptions || isObjectSkip; + + let queryReference: Query | undefined; + let argsObject: Record = {}; + let throwOnError = false; + let initialValue: Query["_returnType"] | undefined; + let preloadedResult: Query["_returnType"] | undefined; + + if (isObjectOptions) { + if ("preloaded" in queryOrOptions) { + const parsed = parsePreloaded(queryOrOptions.preloaded); + queryReference = parsed.queryReference; + argsObject = parsed.argsObject; + preloadedResult = parsed.preloadedResult; + throwOnError = queryOrOptions.throwOnError ?? false; + } else { + const query = queryOrOptions.query; + queryReference = + typeof query === "string" + ? (makeFunctionReference<"query", any, any>(query) as Query) + : query; + argsObject = queryOrOptions.args ?? ({} as Record); + throwOnError = queryOrOptions.throwOnError ?? false; + initialValue = queryOrOptions.initialValue; + } + } else if (isLegacy) { + const query = queryOrOptions as Query; + queryReference = + typeof query === "string" + ? (makeFunctionReference<"query", any, any>(query) as Query) + : query; + argsObject = legacySkip ? {} : parseArgs(args[0] as Query["_args"]); + } - const queryName = getFunctionName(queryReference); + const skip = isObjectSkip || legacySkip; + const queryName = queryReference ? getFunctionName(queryReference) : ""; const queries = useMemo( () => - skip + skip || !queryReference ? ({} as RequestForQueries) : { query: { query: queryReference, args: argsObject } }, // Stringify args so args that are semantically the same don't trigger a @@ -844,10 +999,46 @@ export function useQuery>( const results = useQueries(queries); const result = results["query"]; + + if (!isObjectReturn) { + if (result instanceof Error) { + throw result; + } + return result; + } + if (result instanceof Error) { - throw result; + if (throwOnError) { + throw result; + } + return { + status: "error", + value: undefined, + error: result, + } satisfies UseQueryResult; } - return result; + + if (result === undefined) { + const fallbackValue = preloadedResult ?? initialValue; + if (fallbackValue !== undefined) { + return { + status: "success", + value: fallbackValue, + error: undefined, + } satisfies UseQueryResult; + } + return { + status: "loading", + value: undefined, + error: undefined, + } satisfies UseQueryResult; + } + + return { + status: "success", + value: result, + error: undefined, + } satisfies UseQueryResult; } /** diff --git a/npm-packages/convex/src/react/hydration.tsx b/npm-packages/convex/src/react/hydration.tsx index f20895825..b0ca231f0 100644 --- a/npm-packages/convex/src/react/hydration.tsx +++ b/npm-packages/convex/src/react/hydration.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { useQuery } from "../react/client.js"; -import { FunctionReference, makeFunctionReference } from "../server/api.js"; -import { jsonToConvex } from "../values/index.js"; +import { FunctionReference } from "../server/api.js"; +import { parsePreloaded } from "./preloaded_utils.js"; /** * The preloaded query payload, which should be passed to a client component @@ -34,17 +34,10 @@ export type Preloaded> = { export function usePreloadedQuery>( preloadedQuery: Preloaded, ): Query["_returnType"] { - const args = useMemo( - () => jsonToConvex(preloadedQuery._argsJSON), - [preloadedQuery._argsJSON], - ) as Query["_args"]; - const preloadedResult = useMemo( - () => jsonToConvex(preloadedQuery._valueJSON), - [preloadedQuery._valueJSON], + const parsed = useMemo( + () => parsePreloaded(preloadedQuery), + [preloadedQuery], ); - const result = useQuery( - makeFunctionReference(preloadedQuery._name) as Query, - args, - ); - return result === undefined ? preloadedResult : result; + const result = useQuery(parsed.queryReference, parsed.argsObject); + return result === undefined ? parsed.preloadedResult : result; } diff --git a/npm-packages/convex/src/react/index.ts b/npm-packages/convex/src/react/index.ts index f7d08f3f0..e0b8bb4dd 100644 --- a/npm-packages/convex/src/react/index.ts +++ b/npm-packages/convex/src/react/index.ts @@ -78,6 +78,9 @@ export { type MutationOptions, type ConvexReactClientOptions, type OptionalRestArgsOrSkip, + type UseQueryOptions, + type UseQueryPreloadedOptions, + type UseQueryResult, ConvexReactClient, useConvex, ConvexProvider, diff --git a/npm-packages/convex/src/react/preloaded_utils.ts b/npm-packages/convex/src/react/preloaded_utils.ts new file mode 100644 index 000000000..b851149d5 --- /dev/null +++ b/npm-packages/convex/src/react/preloaded_utils.ts @@ -0,0 +1,29 @@ +import { + FunctionArgs, + FunctionReference, + makeFunctionReference, +} from "../server/api.js"; +import { jsonToConvex } from "../values/index.js"; +import type { Preloaded } from "./hydration.js"; + +/** + * Parse a preloaded query payload into its constituent parts. + * + * This is a hook-free helper that can be used by both `useQuery` and + * `usePreloadedQuery` to avoid duplicating the parsing logic. + * + * @internal + */ +export function parsePreloaded>( + preloaded: Preloaded, +): { + queryReference: Query; + argsObject: FunctionArgs; + preloadedResult: Query["_returnType"]; +} { + return { + queryReference: makeFunctionReference(preloaded._name) as Query, + argsObject: jsonToConvex(preloaded._argsJSON) as FunctionArgs, + preloadedResult: jsonToConvex(preloaded._valueJSON) as Query["_returnType"], + }; +} From 56bb8980c2882872da1e79e23fb109fd4f89d06a Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 11:06:50 -0800 Subject: [PATCH 2/8] test --- .../convex/src/react/use_query.test.ts | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/npm-packages/convex/src/react/use_query.test.ts b/npm-packages/convex/src/react/use_query.test.ts index 543a777d8..79e966753 100644 --- a/npm-packages/convex/src/react/use_query.test.ts +++ b/npm-packages/convex/src/react/use_query.test.ts @@ -3,11 +3,12 @@ */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { test, describe, expect } from "vitest"; +import { test, describe, expect, expectTypeOf } from "vitest"; import { anyApi } from "../server/api.js"; -import { ApiFromModules, QueryBuilder } from "../server/index.js"; +import type { ApiFromModules, QueryBuilder } from "../server/index.js"; import { useQuery as useQueryReal } from "./client.js"; +import type { Preloaded } from "./hydration.js"; // Intentional noop, we're just testing types. const useQuery = (() => {}) as unknown as typeof useQueryReal; @@ -68,4 +69,70 @@ describe("useQuery types", () => { // @ts-expect-error adding args is not allowed useQuery(api.module.noArgs, { _arg: 1 }); }); + + test("Queries with object options", () => { + useQuery({ + query: api.module.noArgs, + }); + + useQuery({ + query: api.module.noArgs, + args: {}, + }); + + useQuery({ + query: api.module.args, + args: { _arg: "asdf" }, + }); + + useQuery({ + query: api.module.args, + args: { _arg: "asdf" }, + initialValue: "initial value", + }); + + useQuery({ + query: api.module.args, + args: { _arg: "asdf" }, + throwOnError: true, + }); + + useQuery({ + query: api.module.args, + args: { _arg: "asdf" }, + skip: true, + }); + + const { + status: _status, + value: _value, + error: _error, + } = useQuery({ + query: api.module.args, + args: { _arg: "asdf" }, + initialValue: "initial value", + throwOnError: true, + }); + if (_status === "success") { + expectTypeOf(_value).toEqualTypeOf("initial value"); + } + if (_status === "error") { + expectTypeOf(_error).toEqualTypeOf(); + } + if (_status === "loading") { + expectTypeOf(_value).toEqualTypeOf(); + } + + useQuery("skip"); + }); + + test("Queries with preloaded options", () => { + const { + status: _status, + value: _value, + error: _error, + } = useQuery({ + preloaded: {} as Preloaded, + }); + }); }); From 4ea93d5eb3342b7c4b0c43dd010906cf9cdebf41 Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 11:20:45 -0800 Subject: [PATCH 3/8] drop skip object arg --- npm-packages/convex/src/react/client.ts | 15 +-------------- npm-packages/convex/src/react/use_query.test.ts | 15 ++++++++++----- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/npm-packages/convex/src/react/client.ts b/npm-packages/convex/src/react/client.ts index c7e998053..b617cd996 100644 --- a/npm-packages/convex/src/react/client.ts +++ b/npm-packages/convex/src/react/client.ts @@ -824,12 +824,6 @@ export type UseQueryOptions> = { * @defaultValue undefined */ initialValue?: Query["_returnType"]; - /** - * When true, the query will not be subscribed and it will behave the same as - * if it was loading. - * @defaultValue false - */ - skip?: boolean; } & (FunctionArgs extends EmptyObject ? { /** @@ -862,12 +856,6 @@ export type UseQueryPreloadedOptions> = * @defaultValue false */ throwOnError?: boolean; - /** - * When true, the query will not be subscribed and it will behave the same as - * if it was loading. - * @defaultValue false - */ - skip?: boolean; }; /** @@ -945,8 +933,7 @@ export function useQuery>( typeof queryOrOptions === "object" && queryOrOptions !== null && ("query" in queryOrOptions || "preloaded" in queryOrOptions); - const isObjectSkip = - queryOrOptions === "skip" || (isObjectOptions && !!queryOrOptions.skip); + const isObjectSkip = queryOrOptions === "skip"; const isLegacy = !isObjectOptions && !isObjectSkip; const legacySkip = isLegacy && args[0] === "skip"; const isObjectReturn = isObjectOptions || isObjectSkip; diff --git a/npm-packages/convex/src/react/use_query.test.ts b/npm-packages/convex/src/react/use_query.test.ts index 79e966753..7778731bd 100644 --- a/npm-packages/convex/src/react/use_query.test.ts +++ b/npm-packages/convex/src/react/use_query.test.ts @@ -97,11 +97,16 @@ describe("useQuery types", () => { throwOnError: true, }); - useQuery({ - query: api.module.args, - args: { _arg: "asdf" }, - skip: true, - }); + const _arg: string | undefined = undefined; + + useQuery( + !_arg + ? "skip" + : { + query: api.module.args, + args: { _arg }, + }, + ); const { status: _status, From 4ea22a8e7bceb2c80d22e1594cb5fcd31cfa78ed Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 12:26:09 -0800 Subject: [PATCH 4/8] value -> data --- npm-packages/convex/src/react/client.ts | 14 +++++++------- npm-packages/convex/src/react/use_query.test.ts | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/npm-packages/convex/src/react/client.ts b/npm-packages/convex/src/react/client.ts index b617cd996..f7fb99cce 100644 --- a/npm-packages/convex/src/react/client.ts +++ b/npm-packages/convex/src/react/client.ts @@ -866,17 +866,17 @@ export type UseQueryPreloadedOptions> = export type UseQueryResult = | { status: "success"; - value: T; + data: T; error: undefined; } | { status: "error"; - value: undefined; + data: undefined; error: Error; } | { status: "loading"; - value: undefined; + data: undefined; error: undefined; }; @@ -1000,7 +1000,7 @@ export function useQuery>( } return { status: "error", - value: undefined, + data: undefined, error: result, } satisfies UseQueryResult; } @@ -1010,20 +1010,20 @@ export function useQuery>( if (fallbackValue !== undefined) { return { status: "success", - value: fallbackValue, + data: fallbackValue, error: undefined, } satisfies UseQueryResult; } return { status: "loading", - value: undefined, + data: undefined, error: undefined, } satisfies UseQueryResult; } return { status: "success", - value: result, + data: result, error: undefined, } satisfies UseQueryResult; } diff --git a/npm-packages/convex/src/react/use_query.test.ts b/npm-packages/convex/src/react/use_query.test.ts index 7778731bd..83bffed96 100644 --- a/npm-packages/convex/src/react/use_query.test.ts +++ b/npm-packages/convex/src/react/use_query.test.ts @@ -110,7 +110,7 @@ describe("useQuery types", () => { const { status: _status, - value: _value, + data: _data, error: _error, } = useQuery({ query: api.module.args, @@ -119,13 +119,13 @@ describe("useQuery types", () => { throwOnError: true, }); if (_status === "success") { - expectTypeOf(_value).toEqualTypeOf("initial value"); + expectTypeOf(_data).toEqualTypeOf("initial value"); } if (_status === "error") { expectTypeOf(_error).toEqualTypeOf(); } if (_status === "loading") { - expectTypeOf(_value).toEqualTypeOf(); + expectTypeOf(_data).toEqualTypeOf(); } useQuery("skip"); @@ -134,7 +134,7 @@ describe("useQuery types", () => { test("Queries with preloaded options", () => { const { status: _status, - value: _value, + data: _data, error: _error, } = useQuery({ preloaded: {} as Preloaded, From 58759bc2801741af07fa0acd1e6cf2e6eb151ca3 Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 18:58:18 -0800 Subject: [PATCH 5/8] move preloaded --- npm-packages/convex/src/react/client.ts | 2 +- npm-packages/convex/src/react/hydration.tsx | 2 +- .../convex/src/react/{preloaded_utils.ts => preloaded.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename npm-packages/convex/src/react/{preloaded_utils.ts => preloaded.ts} (100%) diff --git a/npm-packages/convex/src/react/client.ts b/npm-packages/convex/src/react/client.ts index f7fb99cce..82b26c074 100644 --- a/npm-packages/convex/src/react/client.ts +++ b/npm-packages/convex/src/react/client.ts @@ -39,7 +39,7 @@ import { ExtendedTransition, } from "../browser/sync/paginated_query_client.js"; import type { Preloaded } from "./hydration.js"; -import { parsePreloaded } from "./preloaded_utils.js"; +import { parsePreloaded } from "./preloaded.js"; // When no arguments are passed, extend subscriptions (for APIs that do this by default) // for this amount after the subscription would otherwise be dropped. diff --git a/npm-packages/convex/src/react/hydration.tsx b/npm-packages/convex/src/react/hydration.tsx index b0ca231f0..abd41b4a0 100644 --- a/npm-packages/convex/src/react/hydration.tsx +++ b/npm-packages/convex/src/react/hydration.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { useQuery } from "../react/client.js"; import { FunctionReference } from "../server/api.js"; -import { parsePreloaded } from "./preloaded_utils.js"; +import { parsePreloaded } from "./preloaded.js"; /** * The preloaded query payload, which should be passed to a client component diff --git a/npm-packages/convex/src/react/preloaded_utils.ts b/npm-packages/convex/src/react/preloaded.ts similarity index 100% rename from npm-packages/convex/src/react/preloaded_utils.ts rename to npm-packages/convex/src/react/preloaded.ts From 4d7d27f2422ec12eb02a83d0e9e17711a309236d Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 18:58:50 -0800 Subject: [PATCH 6/8] don't export types for now --- npm-packages/convex/src/react/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/npm-packages/convex/src/react/index.ts b/npm-packages/convex/src/react/index.ts index e0b8bb4dd..bd879ad4f 100644 --- a/npm-packages/convex/src/react/index.ts +++ b/npm-packages/convex/src/react/index.ts @@ -78,8 +78,6 @@ export { type MutationOptions, type ConvexReactClientOptions, type OptionalRestArgsOrSkip, - type UseQueryOptions, - type UseQueryPreloadedOptions, type UseQueryResult, ConvexReactClient, useConvex, From 44a46774b1f2bc775734280a6dcc8d2da2d17fbd Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 18:59:35 -0800 Subject: [PATCH 7/8] rename ConvexQueryOptions to QueryOptions for now --- .../convex/src/browser/query_options.ts | 17 +++++++++-------- npm-packages/convex/src/react/client.ts | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/npm-packages/convex/src/browser/query_options.ts b/npm-packages/convex/src/browser/query_options.ts index caae1c0ca..7ac46041e 100644 --- a/npm-packages/convex/src/browser/query_options.ts +++ b/npm-packages/convex/src/browser/query_options.ts @@ -5,22 +5,23 @@ */ import type { FunctionArgs, FunctionReference } from "../server/api.js"; -// TODO if this type can encompass all use cases we can add not requiring args for queries -// that don't take arguments. Goal would be that queryOptions allows leaving out args, -// but queryOptions returns an object that always contains args. Helpers, "middleware," -// anything that intercepts these arguments /** * Query options. */ -export type ConvexQueryOptions> = { +export type QueryOptions> = { + /** + * The query function to run. + */ query: Query; + /** + * The arguments to the query function. + */ args: FunctionArgs; - extendSubscriptionFor?: number; }; // This helper helps more once we have more inference happening. export function convexQueryOptions>( - options: ConvexQueryOptions, -): ConvexQueryOptions { + options: QueryOptions, +) { return options; } diff --git a/npm-packages/convex/src/react/client.ts b/npm-packages/convex/src/react/client.ts index 82b26c074..6aca3ed49 100644 --- a/npm-packages/convex/src/react/client.ts +++ b/npm-packages/convex/src/react/client.ts @@ -32,12 +32,12 @@ import { instantiateNoopLogger, Logger, } from "../browser/logging.js"; -import { ConvexQueryOptions } from "../browser/query_options.js"; import { LoadMoreOfPaginatedQuery } from "../browser/sync/pagination.js"; import { PaginatedQueryClient, ExtendedTransition, } from "../browser/sync/paginated_query_client.js"; +import type { QueryOptions } from "../browser/query_options.js"; import type { Preloaded } from "./hydration.js"; import { parsePreloaded } from "./preloaded.js"; @@ -539,7 +539,7 @@ export class ConvexReactClient { * an optional extendSubscriptionFor for how long to subscribe to the query. */ prewarmQuery>( - queryOptions: ConvexQueryOptions & { + queryOptions: QueryOptions & { extendSubscriptionFor?: number; }, ) { From ccad381ca9d862255abd093ac48553bf69a9ac08 Mon Sep 17 00:00:00 2001 From: Ian Macartney Date: Thu, 20 Nov 2025 19:00:47 -0800 Subject: [PATCH 8/8] unify with QueryOptions and require args --- npm-packages/convex/src/react/client.ts | 44 ++++++------------- .../convex/src/react/use_query.test.ts | 1 + 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/npm-packages/convex/src/react/client.ts b/npm-packages/convex/src/react/client.ts index 6aca3ed49..12e8b2b83 100644 --- a/npm-packages/convex/src/react/client.ts +++ b/npm-packages/convex/src/react/client.ts @@ -808,36 +808,20 @@ export type OptionalRestArgsOrSkip> = * * @public */ -export type UseQueryOptions> = { - /** - * The query function to run. - */ - query: Query; - /** - * Whether to throw an error if the query fails. - * If false, the error will be returned in the `error` field. - * @defaultValue false - */ - throwOnError?: boolean; - /** - * An initial value to use before the query result is available. - * @defaultValue undefined - */ - initialValue?: Query["_returnType"]; -} & (FunctionArgs extends EmptyObject - ? { - /** - * The arguments to the query function. - * Optional for queries with no arguments. - */ - args?: FunctionArgs; - } - : { - /** - * The arguments to the query function. - */ - args: FunctionArgs; - }); +export type UseQueryOptions> = + QueryOptions & { + /** + * Whether to throw an error if the query fails. + * If false, the error will be returned in the `error` field. + * @defaultValue false + */ + throwOnError?: boolean; + /** + * An initial value to use before the query result is available. + * @defaultValue undefined + */ + initialValue?: Query["_returnType"]; + }; /** * Options for the object-based {@link useQuery} overload with a preloaded query. diff --git a/npm-packages/convex/src/react/use_query.test.ts b/npm-packages/convex/src/react/use_query.test.ts index 83bffed96..503dee2fb 100644 --- a/npm-packages/convex/src/react/use_query.test.ts +++ b/npm-packages/convex/src/react/use_query.test.ts @@ -73,6 +73,7 @@ describe("useQuery types", () => { test("Queries with object options", () => { useQuery({ query: api.module.noArgs, + args: {}, }); useQuery({