useActorQuery
useActorQuery is a React hook for fetching data from Internet Computer canisters. It wraps TanStack Query’s useQuery with canister-specific functionality.
Import
Section titled “Import”import { createActorHooks } from "@ic-reactor/react"import { backend } from "./reactor"
const { useActorQuery } = createActorHooks(backend)import { useActorQuery } from "./reactor"
function UserProfile({ userId }: { userId: string }) { const { data, isPending, error } = useActorQuery({ functionName: "get_user", args: [userId], })
if (isPending) return <div>Loading...</div> if (error) return <div>Error: {error.message}</div>
return <div>{data?.name}</div>}Options
Section titled “Options”UseActorQueryParameters extends react-query’s QueryObserverOptions, giving you access to all standard react-query options plus our custom actor options.
Required Options
Section titled “Required Options”| Option | Type | Description |
|---|---|---|
functionName |
string |
The canister method to call |
Optional Options
Section titled “Optional Options”| Option | Type | Default | Description |
|---|---|---|---|
args |
array | skipToken |
[] |
Arguments to pass to the method, or skipToken while they are not known |
enabled |
boolean |
true |
Whether the query should run |
staleTime |
number |
0 |
Time in ms before data is considered stale |
gcTime |
number |
300000 |
Time in ms before unused data is garbage collected |
refetchInterval |
number | false |
false |
Polling interval in ms |
refetchOnWindowFocus |
boolean |
true |
Refetch when window regains focus |
refetchOnMount |
boolean |
true |
Refetch when component mounts |
refetchOnReconnect |
boolean |
true |
Refetch when network reconnects |
retry |
number | boolean | function |
QueryClient’s | Retry policy (update methods) |
retryDelay |
number | function |
exponential | Delay between retries |
select |
function |
- | Transform the data before returning |
queryKey |
array |
- | Additional query key segments |
callConfig |
CallConfig |
- | IC agent call configuration |
Return Value
Section titled “Return Value”Returns a TanStack Query result object:
| Property | Type | Description |
|---|---|---|
data |
TData | undefined |
The resolved data |
error |
Error | null |
Error if query failed |
status |
'pending' | 'error' | 'success' |
Query status |
isPending |
boolean |
True if no data yet |
isError |
boolean |
True if query errored |
isSuccess |
boolean |
True if query succeeded |
isFetching |
boolean |
True if fetching (including background) |
isRefetching |
boolean |
True if refetching |
refetch |
function |
Manually trigger a refetch |
dataUpdatedAt |
number |
Timestamp of last data update |
Examples
Section titled “Examples”Basic Query
Section titled “Basic Query”import { useActorQuery } from "./reactor"
function Greeting() { const { data, isPending, error } = useActorQuery({ functionName: "greet", args: ["World"], })
if (isPending) return <span>Loading…</span> if (error) return <span>Error: {error.message}</span> return <span>{data}</span>}Waiting for Args
Section titled “Waiting for Args”When the arguments are not known yet, pass TanStack Query’s skipToken
(re-exported by @ic-reactor/react) in their place. The query waits without
calling the canister, and fetches as soon as args holds real arguments:
import { skipToken } from "@ic-reactor/react"import { useActorQuery } from "./reactor"
function UserProfile({ userId }: { userId: string | undefined }) { const { data } = useActorQuery({ functionName: "get_user", // No `userId!`, no placeholder id, no `enabled` args: userId ? [userId] : skipToken, })
return <div>{data?.name}</div>}A skipped query has a cache entry of its own under its method’s key (at the
canister callConfig names), which no call’s key shares: it shows no data
until its arguments arrive, not even that of a query of the method made
without arguments. refetch() has nothing to run until then, and TanStack
Query answers it with a “Missing queryFn” error, so offer a refresh only once
the arguments exist.
Conditional Query
Section titled “Conditional Query”Use enabled when the arguments are known but the query should still wait,
for a reason of your own:
import { useActorQuery, useAuth } from "./reactor"
function CurrentUser() { const { isAuthenticated } = useAuth()
const { data } = useActorQuery({ functionName: "get_user", args: [userId], enabled: isAuthenticated, // Only fetch once signed in })
return <div>{data?.name}</div>}With Select
Section titled “With Select”import { useActorQuery } from "./reactor"
function UserName() { // Extract only the name from the user object const { data: userName } = useActorQuery({ functionName: "get_user", args: [userId], select: (user) => user?.name, })
return <span>{userName}</span>}Stale Time Configuration
Section titled “Stale Time Configuration”import { useActorQuery } from "./reactor"
function Settings() { // Data stays fresh for 5 minutes const { data } = useActorQuery({ functionName: "get_config", args: [], staleTime: 5 * 60 * 1000, })
// Data is never stale const { data: metadata } = useActorQuery({ functionName: "get_metadata", args: [], staleTime: Infinity, })
return <pre>{JSON.stringify({ data, metadata })}</pre>}Polling
Section titled “Polling”import { useActorQuery } from "./reactor"
function Price() { // Refetch every 10 seconds const { data } = useActorQuery({ functionName: "get_price", args: [], refetchInterval: 10_000, })
return <span>{String(data)}</span>}Manual Refetch
Section titled “Manual Refetch”import { useActorQuery } from "./reactor"
function Notifications() { const { data, refetch, isFetching } = useActorQuery({ functionName: "get_notifications", args: [], })
return ( <div> <NotificationList data={data} /> <button onClick={() => refetch()} disabled={isFetching}> Refresh </button> </div> )}Error Handling
Section titled “Error Handling”A method that returns a Result resolves with its Ok value. Its Err arm is
thrown as a CanisterError, which isCanisterError narrows, so a business
logic failure is told apart from a network one:
import { isCanisterError } from "@ic-reactor/react"import { useActorQuery } from "./reactor"
function Balance() { // `get_balance` returns `{ Ok: nat } | { Err: BalanceError }` const { data, error, isError } = useActorQuery({ functionName: "get_balance", args: [principal.toText()], })
if (isError) { if (isCanisterError(error)) { // Business logic error from canister return ( <div> Canister error [{error.code}]: {JSON.stringify(error.err)} </div> ) } // Network or other error return <div>Error: {error.message}</div> }
return <div>Balance: {data}</div>}Type-Safe Queries
Section titled “Type-Safe Queries”import { useActorQuery } from "./reactor"
function User() { // TypeScript infers types from your canister interface const { data } = useActorQuery({ functionName: "get_user", // ✅ Autocompletes valid methods args: ["user-123"], // ✅ Type-checked }) // data is typed as User | undefined
return <span>{data?.name}</span>}With Custom Query Key
Section titled “With Custom Query Key”import { useActorQuery } from "./reactor"
function Data() { // Add custom segments to the query key const { data } = useActorQuery({ functionName: "get_data", args: [], queryKey: ["version", "2"], }) // Full key: [canisterId, "get_data", "[]", "version", "2"]
return <pre>{JSON.stringify(data)}</pre>}The args segment is the JSON-serialized args array, so an empty args: []
still contributes the literal "[]". Custom queryKey segments are appended
after it, never in place of the identity prefix.
Dependent Queries
Section titled “Dependent Queries”import { skipToken } from "@ic-reactor/react"import { useActorQuery } from "./reactor"
function UserPosts({ userId }: { userId: string }) { // First query const { data: user } = useActorQuery({ functionName: "get_user", args: [userId], })
// Dependent query - waits until the first one has the user's id const { data: posts } = useActorQuery({ functionName: "get_user_posts", args: user ? [user.id] : skipToken, })
return <PostList posts={posts} />}Type Parameters
Section titled “Type Parameters”useActorQuery< M extends FunctionName<A>, // Method name TSelected = ReturnType // Type after select>(options: UseActorQueryParameters<A, M, T, TSelected>)The types are automatically inferred from your actor interface, so you typically don’t need to specify them manually.
See Also
Section titled “See Also”- Queries Guide — In-depth guide to queries
- useActorSuspenseQuery — Suspense version
- useActorInfiniteQuery — For pagination
- createActorHooks — Creating hooks