createReactorProvider
createReactorProvider<
TValue,TProps>(factory,options?):CreateReactorProviderReturn<TValue,NonNullable<TProps>>
Defined in: react/src/createReactorProvider.ts:196
Creates a provider that builds an app’s reactors once per mounted tree, and
a useReactor() hook that returns what it built, fully typed.
This is the setup a server-rendered app needs. The factory runs in a
useState initializer, once per mounted tree, and a server render is a tree
of its own: each request builds its own ClientManager, QueryClient,
reactors and AuthenticationManager, so no cache or identity is shared
between visitors. The first render needs no effect, so the server’s HTML is
complete, and the browser builds a fresh value from the same props to
hydrate it. In a client-only app it works the same, one value per mount.
The factory returns anything: a defineReactor or defineDisplayReactor
result, a record of them, reactors and managers built by hand, query and
mutation objects from createQuery or createMutation. useReactor()
returns it with the factory’s own return type, so its hooks keep their
generic signatures. useReactor("todo") returns one property of it.
When the tree unmounts, the provider disposes each AuthenticationManager
built for the value, releasing the Internet Identity client it built: those
constructed while the factory ran, and the one a defineReactor result in
the value, or among its own properties, builds on first use (a result that
never used authentication builds none). A manager built elsewhere and
handed in, such as an app-wide one passed as authentication or as a
prop, is left to whoever built it. dispose() only forgets the client, and
the next sign-in builds a new one, so this is safe under StrictMode, which
runs the cleanup and then the effect again on the same value.
It also renders a QueryClientProvider for the value’s QueryClient; see
CreateReactorProviderOptions.queryClientProvider.
A suspense hook below the provider may suspend its first render: React
renders the same element again when the data arrives, and the provider
reuses the value that first render built, whether the Suspense boundary
sits above the provider or, while hydrating, there is none. A provider that
a transition mounts (startTransition, a client-side navigation) can be
rendered from a new element on each retry and build a new value each time,
so give its suspending components a <Suspense> boundary inside it.
Call createReactorProvider at module scope: it builds nothing itself,
only the context, and every mounted provider builds its own value. In the
Next.js App Router that module needs "use client", like any module with
hooks; a server component renders the provider from there.
Type Parameters
Section titled “Type Parameters”TValue
Section titled “TValue”TValue extends object
TProps
Section titled “TProps”TProps extends object | undefined = { }
Parameters
Section titled “Parameters”factory
Section titled “factory”(props) => TValue
Builds the value. It receives the provider’s props other
than children, and runs once per mounted provider.
options?
Section titled “options?”CreateReactorProviderOptions = {}
See CreateReactorProviderOptions.
Returns
Section titled “Returns”CreateReactorProviderReturn<TValue, NonNullable<TProps>>
Examples
Section titled “Examples”One canister, with Internet Identity
"use client"import { createReactorProvider, defineReactor } from "@ic-reactor/react"import { canisterId, idlFactory, type _SERVICE } from "./declarations/todo"
export const { ReactorProvider, useReactor } = createReactorProvider(() => defineReactor<_SERVICE>({ name: "todo", idlFactory, canisterId }))
function Todos() { const { useActorQuery, useAuth } = useReactor() const { isAuthenticated } = useAuth() const { data } = useActorQuery({ functionName: "getAllTodos" }) return <p>{isAuthenticated ? data?.length : "Sign in"}</p>}
// app/layout.tsx (a server component)// <ReactorProvider>{children}</ReactorProvider>Several canisters sharing one agent and one sign-in
export const { ReactorProvider, useReactor } = createReactorProvider(() => { const backend = defineReactor<Backend>({ name: "backend", idlFactory: backendIdl, canisterId: backendId, }) const ledger = defineDisplayReactor<Ledger>({ name: "ledger", idlFactory: ledgerIdl, canisterId: ledgerId, authentication: backend.authentication, }) return { backend, ledger }})
function Balance() { const { data } = useReactor("ledger").useActorQuery({ functionName: "icrc1_total_supply", }) return <span>{data}</span>}Props for the factory, and a new key to build again
export const { ReactorProvider: LedgerProvider, useReactor: useLedger } = createReactorProvider(({ canisterId }: { canisterId: string }) => defineDisplayReactor<Ledger>({ name: "ledger", idlFactory, canisterId }) )
// Read once per mount: the key makes a new canister a new tree.<LedgerProvider key={canisterId} canisterId={canisterId}> <TokenPage /></LedgerProvider>