ReactorProvider
ReactorProvider(
__namedParameters):ReactElement
Defined in: react/src/index.tsx:380
Gives a tree one client: it gets it from client once, on the first
render, makes it available to useClient and useAuth, renders
TanStack Query’s QueryClientProvider around the children with the
client’s QueryClient, and, if its factory created the client, disposes it
when it unmounts.
Put it at the root of the part of the app that calls canisters, in a client module (the examples below). On a server it renders inside the request, so a factory that creates the client runs once per request and no cache or caller is shared between two users, which a client at module scope would be.
Who disposes the client depends on when it was created, not on where the
factory is written. A client the factory call creates belongs to the
provider. A client created before the call, such as one at module scope that
code outside React uses too, is borrowed: the provider never disposes it,
however many times it mounts and unmounts, and the app ends it with
client.dispose() if it ever needs to. A factory that creates the shared
client lazily on its first call (() => (client ??= createClient(...)))
hands that first provider ownership, and the provider disposes it on
unmount. It can also lose the client before any unmount: when React throws
away the render that created it (see below), the client stays registered
for disposal at collection until the next render gets it back from the
factory, so a garbage collection while the fallback shows disposes the
client the retry then mounts. Create a shared client eagerly instead. In
development, a provider whose factory hands back a client that an earlier
factory call created and no provider has committed yet, as a lazily shared
factory does, logs a warning (under StrictMode, on its first render), and
a provider that is given a borrowed client which is already disposed logs an
error.
Disposal is safe in React’s development double-mount (StrictMode mounts,
unmounts and mounts every component again at once): the cleanup only
schedules the disposal for the next macrotask, and the second mount cancels
it, so a client in use is never disposed. An unmount that is not followed
by a mount disposes an owned client exactly once.
React runs no cleanup for a render it throws away before committing it: the
first render of a provider below a Suspense boundary that suspends, or the
useState initializer call that StrictMode drops in development. An owned
client built for such a render, whose auth a child’s useAuth may
already have built, is disposed once that render’s state is garbage
collected, through a FinalizationRegistry. That is later than an unmount
would dispose it, at a moment no code chooses, but it is disposed, auth and
listeners with it. A client that the factory returns again, or that a
provider commits, is taken off the registry, whoever owns it. A runtime
without FinalizationRegistry never disposes such a client, and a server
never registers one: it builds no auth there, and the rest of the request
still uses it.
React also runs the effects of a subtree again when it shows a hidden
Activity once more, and that can be long after they were cleaned up. If an
owned client was disposed meanwhile, the provider builds another from the
same factory, because a disposed client cannot call, sign in or cache any
more. The cost is that hiding a provider inside an Activity drops its
cache: disposing clears the QueryClient, and the client built on the next
show starts empty. React cannot tell a hidden subtree from an unmounted one
in the cleanup, so there is no way to keep it. To keep a cache across
hiding, render the provider above the Activity, not inside it, or give it
a borrowed client, which is never disposed.
Parameters
Section titled “Parameters”__namedParameters
Section titled “__namedParameters”Returns
Section titled “Returns”ReactElement
Examples
Section titled “Examples”A client per provider, owned and disposed by it (on a server, one per request):
"use client"
import { createClient } from "@ic-reactor/core"import { ReactorProvider } from "@ic-reactor/react"import { AuthClient } from "@icp-sdk/auth/client"import type { ReactNode } from "react"
export function Providers({ children }: { children: ReactNode }) { return ( <ReactorProvider client={() => createClient({ network: "ic", auth: () => new AuthClient() }) } > {children} </ReactorProvider> )}One client per tab, created at module scope and used outside React too. The provider borrows it and never disposes it (browser-only: on a server a module-scope client is shared by every request):
"use client"
import { createClient } from "@ic-reactor/core"import { ReactorProvider } from "@ic-reactor/react"import { AuthClient } from "@icp-sdk/auth/client"import type { ReactNode } from "react"
export const client = createClient({ network: "ic", auth: () => new AuthClient(),})
export function Providers({ children }: { children: ReactNode }) { return <ReactorProvider client={() => client}>{children}</ReactorProvider>}