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.timeToLivekeeps the result after the last reader;Infinitykeeps it alive.mutation(): a function atom, set with{ input, reactivityKeys }.subscription(input): an atom of the latest event.trpc.clientis the Effect client the atoms call;trpc.runtimeis 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.