Execute a command from React
Goal: a user clicks a button or fills a form, and a command runs — with validation feedback, a disabled-while-running button, and no hand-written API client.
Call the proxy, don’t write a client
Section titled “Call the proxy, don’t write a client”After a configured Debug proxy-generation build, Arc supplies typed command proxies. This recipe assumes the standalone backend and full-stack tutorial setup, which supplies the Vite bootstrap, Components prerequisites, providers, and dialog composition. For an inline form in an existing Vite app, frontend getting started uses Arc’s CommandForm with Components fields, and shows the route without Components. You don’t write a fetch, redeclare the command’s shape, or wire validation — the proxy already knows the types, and CommandDialog drives the whole interaction.
-
Use
CommandDialogfor a form. Pass the command constructor; the dialog instantiates it, renders the confirm/cancel buttons, and disables confirm while it executes. Use command form fields for the inputs:AddAuthor.tsx import { useState } from 'react';import { Guid } from '@cratis/fundamentals';import { CommandDialog } from '@cratis/components/CommandDialog';import { InputTextField } from '@cratis/components/CommandForm';import { RegisterAuthor } from './Authors/RegisterAuthor'; // generated proxyexport const AddAuthor = () => {const [id] = useState(() => Guid.create());return (<CommandDialog<RegisterAuthor> command={RegisterAuthor} title="Add author" okLabel="Add" initialValues={{ id }}><InputTextField<RegisterAuthor> value={i => i.name} title="Name" /></CommandDialog>);};Mount a new dialog instance per operation and unmount on close.
idis stable during editing and fresh for the next registration; a required Guid is not automatically filled by the proxy.The field accessor
i => i.nameis a property on the generated type — rename it in C#, rebuild, and the frontend stops compiling until you fix it. -
Inject context the form needs to be valid via
initialValues. A parent id the user shouldn’t type goes here — not inonBeforeExecute, which fires too late to affect validity:This form fragment assumes the book dialog’s imports and per-operation
bookIdstate. KeepauthorIdas the generatedGuid, not a string.<CommandDialog<AddBook> command={AddBook} title="Add book" initialValues={{ authorId, bookId }}><InputTextField<AddBook> value={i => i.title} title="Title" /></CommandDialog> -
Validation surfaces itself. Supported rules extracted from the command and its value types run client-side; the server remains authoritative, including async/custom/state-dependent rules that cannot be extracted, and
CommandDialogrenders the messages against the fields. You write none of that wiring.
For executing a command without a dialog (a plain button, custom flow), the proxy exposes an execute call that returns a CommandResult — check isSuccess and read any returned value off it.
See also
Section titled “See also”- Dialogs —
CommandDialog,Dialog, and the form-field components. - Command Result — handling the result in code.
- Validate a command — the rules that surface in the form.