Skip to content
tRPC

React atoms

Query, mutation and subscription atoms on Effect Atom, typed with each procedure's errors.

On this page

@trpc12/experimental-react-atom is modeled on Effect's AtomRpc. Each query is an atom of its AsyncResult, typed with the procedure's errors; the same call returns the same atom. Like @trpc12/experimental-react, the whole package is experimental.

import { 
const useAtomSuspense: <A, E, const IncludeFailure extends boolean = false>(atom: Atom<AsyncResult<A, E>>, options?: {
    readonly suspendOnWaiting?: boolean | undefined;
    readonly includeFailure?: IncludeFailure | undefined;
}) => Success<A, E> | (IncludeFailure extends true ? Failure<A, E> : never)
useAtomSuspense
,
const useAtomSet: <R, W, Mode extends "value" | "promise" | "promiseExit" = never>(atom: Writable<R, W>, options?: {
    readonly mode?: ([R] extends [AsyncResult<any, any>] ? Mode : "value") | undefined;
}) => "promise" extends Mode ? ((value: W) => Promise<AsyncResult.Success<R>>) : "promiseExit" extends Mode ? ((value: W) => Promise<Exit<AsyncResult.Success<R>, AsyncResult.Failure<R>>>) : ((value: W | ((value: R) => W)) => void)
useAtomSet
} from '@effect/atom-react';
import { function createAtomClient<TSource, const TLinks extends ReadonlyArray<TrpcLink<any, RouterOf<TSource>>>>(opts: AtomClientOptions<TSource, TLinks>): AtomClient<EffectClient<RouterOf<TSource>, LinksContext<TLinks>, LinksTypes<TLinks>>>createAtomClient } from '@trpc12/experimental-react-atom'; export const
const trpc: AtomClient<EffectClient<{
    post: {
        rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
            title: z.ZodString;
        }, z.core.$strip>>, "mutation", {
            output: Post | undefined;
            errors: never;
            services: never;
            unmapped: never;
        }>;
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<...>;
        create: BuildProcedure<...>;
    };
}, {}, LinksTypes<...>>>
trpc
=
createAtomClient<RouterType<{
    post: {
        rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
            title: z.ZodString;
        }, z.core.$strip>>, "mutation", {
            output: Post | undefined;
            errors: never;
            services: never;
            unmapped: never;
        }>;
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<...>;
        create: BuildProcedure<...>;
    };
}>, readonly [...]>(opts: AtomClientOptions<...>): AtomClient<...>
createAtomClient
({
TypedClientOptions<RouterType<{ post: { rename: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; title: ZodString; }, $strip>>, "mutation", { ...; }>; byId: BuildProcedure<...>; list: BuildProcedure<...>; create: BuildProcedure<...>; }; }>, readonly [...]>.router: RouterType<{
    post: {
        rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
            title: z.ZodString;
        }, z.core.$strip>>, "mutation", {
            output: Post | undefined;
            errors: never;
            services: never;
            unmapped: never;
        }>;
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<...>;
        create: BuildProcedure<...>;
    };
}>
router
:
routerType<{
    post: {
        rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
            title: z.ZodString;
        }, z.core.$strip>>, "mutation", {
            output: Post | undefined;
            errors: never;
            services: never;
            unmapped: never;
        }>;
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<...>;
        create: BuildProcedure<...>;
    };
}>(): RouterType<...>
routerType
<
type AppRouter = {
    post: {
        rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
            title: z.ZodString;
        }, z.core.$strip>>, "mutation", {
            output: Post | undefined;
            errors: never;
            services: never;
            unmapped: never;
        }>;
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<...>;
        create: BuildProcedure<...>;
    };
}
AppRouter
>(),
TypedClientOptions<RouterType<{ post: { rename: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; title: ZodString; }, $strip>>, "mutation", { ...; }>; byId: BuildProcedure<...>; list: BuildProcedure<...>; create: BuildProcedure<...>; }; }>, readonly [...]>.links: readonly [TrpcLink<{
    form: DansonForm;
    provides: "response";
}, any>]
links
: [
httpLink<{
    url: string;
    serializer: Serializer<DansonForm>;
}>(opts: {
    url: string;
    serializer: Serializer<DansonForm>;
}): TrpcLink<{
    form: DansonForm;
    provides: "response";
}, any>
httpLink
({ url: stringurl: '/trpc', serializer: Serializer<DansonForm>serializer: function createSerializer(opts?: SerializerOptions): SerializercreateSerializer() })],
}); function
function Post(props: {
    id: string;
}): React.JSX.Element
Post
(
props: {
    id: string;
}
props
: { id: stringid: string }) {
const
const post: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
post
=
useAtomSuspense<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}, ClientError<TrpcError<"NOT_FOUND", {
    id: string;
}> | InputValidationError<{
    readonly code: "invalid_type";
    readonly expected: z.core.$ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | readonly ["id"];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>>, false>(atom: Atom<...>, options?: {
    ...;
} | undefined): Success<...>
useAtomSuspense
(
const trpc: AtomClient<EffectClient<{
    post: {
        rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
            title: z.ZodString;
        }, z.core.$strip>>, "mutation", {
            output: Post | undefined;
            errors: never;
            services: never;
            unmapped: never;
        }>;
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<...>;
        create: BuildProcedure<...>;
    };
}, {}, LinksTypes<...>>>
trpc
.
post: AtomProcedures<EffectClient<{
    rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
        id: z.ZodString;
        title: z.ZodString;
    }, z.core.$strip>>, "mutation", {
        output: Post | undefined;
        errors: never;
        services: never;
        unmapped: never;
    }>;
    byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
        id: z.ZodString;
    }, z.core.$strip>>, "query", {
        output: Post;
        errors: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        services: never;
        unmapped: never;
    }>;
    list: BuildProcedure<...>;
    create: BuildProcedure<...>;
}, {}, LinksTypes<...>>>
post
.
byId: {
    query(input: {
        id: string;
    }, options?: QueryAtomOptions): Atom<AsyncResult<{
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    }, ClientError<TrpcError<"NOT_FOUND", {
        id: string;
    }> | InputValidationError<{
        readonly code: "invalid_type";
        readonly expected: z.core.$ZodInvalidTypeExpected;
        readonly path: readonly [] | readonly ["id"];
        readonly message: string;
    } | {
        readonly code: "too_big";
        readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
        readonly maximum: string | number;
        readonly inclusive?: boolean | undefined;
        readonly exact?: boolean | undefined;
        readonly path: readonly [] | readonly [...];
        readonly message: string;
    } | ... 9 more ... | {
        ...;
    }>>>>;
}
byId
.
function query(input: {
    id: string;
}, options?: QueryAtomOptions): Atom<AsyncResult<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}, ClientError<TrpcError<"NOT_FOUND", {
    id: string;
}> | InputValidationError<{
    readonly code: "invalid_type";
    readonly expected: z.core.$ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | readonly [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>>>>
query
({ id: stringid:
props: {
    id: string;
}
props
.id: stringid }, { QueryAtomOptions.reactivityKeys?: ReactivityKeys | undefinedreactivityKeys: ['posts'] }),
).
Success<{ id: string; title: string; author: string | null; createdAt: Date; }, ClientError<TrpcError<"NOT_FOUND", { id: string; }> | InputValidationError<{ readonly code: "invalid_type"; readonly expected: $ZodInvalidTypeExpected; readonly path: readonly [] | readonly [...]; readonly message: string; } | ... 10 more ... | { ...; }>>>.value: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
value
;
const
const rename: (value: MutationArg<{
    id: string;
    title: string;
}> | typeof Reset | typeof Interrupt | ((value: AsyncResult<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
} | undefined, ClientError<InputValidationError<{
    readonly code: "invalid_type";
    readonly expected: z.core.$ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"] | readonly ["title"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | ... 1 more ... | readonly [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>>>) => MutationArg<...> | ... 1 more ... | typeof Interrupt)) => void
rename
=
useAtomSet<AsyncResult<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
} | undefined, ClientError<InputValidationError<{
    readonly code: "invalid_type";
    readonly expected: z.core.$ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"] | readonly ["title"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | readonly ["id"] | readonly ["title"];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>>>, MutationArg<...> | ... 1 more ... | typeof Interrupt, never>(atom: Writable<...>, options?: {
    ...;
} | undefined): (value: MutationArg<...> | ... 2 more ... | ((value: AsyncResult<...>) => MutationArg<...> | ... 1 more ... | typeof Interrupt)) => void
useAtomSet
(
const trpc: AtomClient<EffectClient<{
    post: {
        rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
            title: z.ZodString;
        }, z.core.$strip>>, "mutation", {
            output: Post | undefined;
            errors: never;
            services: never;
            unmapped: never;
        }>;
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<...>;
        create: BuildProcedure<...>;
    };
}, {}, LinksTypes<...>>>
trpc
.
post: AtomProcedures<EffectClient<{
    rename: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
        id: z.ZodString;
        title: z.ZodString;
    }, z.core.$strip>>, "mutation", {
        output: Post | undefined;
        errors: never;
        services: never;
        unmapped: never;
    }>;
    byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
        id: z.ZodString;
    }, z.core.$strip>>, "query", {
        output: Post;
        errors: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        services: never;
        unmapped: never;
    }>;
    list: BuildProcedure<...>;
    create: BuildProcedure<...>;
}, {}, LinksTypes<...>>>
post
.
rename: {
    mutation(): AtomResultFn<MutationArg<{
        id: string;
        title: string;
    }>, {
        id: string;
        title: string;
        author: string | null;
        createdAt: Date;
    } | undefined, ClientError<InputValidationError<{
        readonly code: "invalid_type";
        readonly expected: z.core.$ZodInvalidTypeExpected;
        readonly path: readonly [] | readonly ["id"] | readonly ["title"];
        readonly message: string;
    } | {
        readonly code: "too_big";
        readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
        readonly maximum: string | number;
        readonly inclusive?: boolean | undefined;
        readonly exact?: boolean | undefined;
        readonly path: readonly [] | ... 1 more ... | readonly [...];
        readonly message: string;
    } | ... 9 more ... | {
        ...;
    }>>>;
}
rename
.
function mutation(): AtomResultFn<MutationArg<{
    id: string;
    title: string;
}>, {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
} | undefined, ClientError<InputValidationError<{
    readonly code: "invalid_type";
    readonly expected: z.core.$ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"] | readonly ["title"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | ... 1 more ... | readonly [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>>>
mutation
());
return ( <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={() =>
const rename: (value: MutationArg<{
    id: string;
    title: string;
}> | typeof Reset | typeof Interrupt | ((value: AsyncResult<{
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
} | undefined, ClientError<InputValidationError<{
    readonly code: "invalid_type";
    readonly expected: z.core.$ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"] | readonly ["title"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | ... 1 more ... | readonly [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>>>) => MutationArg<...> | ... 1 more ... | typeof Interrupt)) => void
rename
({
MutationArg<{ id: string; title: string; }>.input: {
    id: string;
    title: string;
}
input
: { id: stringid:
props: {
    id: string;
}
props
.id: stringid, title: stringtitle: 'New' },
MutationArg<{ id: string; title: string; }>.reactivityKeys?: ReactivityKeys | undefinedreactivityKeys: ['posts'], }) } > {
const post: {
    id: string;
    title: string;
    author: string | null;
    createdAt: Date;
}
post
.title: stringtitle}
</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button> ); }
  • query(input, { reactivityKeys, timeToLive }): a mutation with the same reactivity keys refetches it. timeToLive keeps the result after the last reader; Infinity keeps it alive.
  • mutation(): a function atom, set with { input, reactivityKeys }.
  • subscription(input): an atom of the latest event.
  • trpc.client is the Effect client the atoms call; trpc.runtime is their runtime.

Server rendering

Query atoms are serializable. On the server, render with a registry that keeps idle atoms (AtomRegistry.make({ defaultIdleTTL })) and call Hydration.dehydrate(registry) afterwards; in the browser, wrap the tree in HydrationBoundary from @effect/atom-react. Results encode with the links' serializer (serializerSchema(serializer) is that Schema, for your own atoms). Failed results aren't carried; the browser runs those queries again.