Hooks & FormSpy
Access form state and the form API from any component inside <FormRenderer>.
All hooks and <FormSpy> must be used inside the <FormRenderer> tree — for example, inside a custom component via componentMapper, or a child component passed via children to FormRenderer.
useFormApi
Returns the programmatic form API.
import { useFormApi } from "@schema-forms-data/renderer";
const api = useFormApi();
Return type
interface FormApi {
change: (name: string, value: unknown) => void;
submit: () => void;
reset: (values?: Record<string, unknown>) => void;
getState: () => FormStateSnapshot;
}
Example
import { useFormApi } from "@schema-forms-data/renderer";
function FillButton() {
const api = useFormApi();
return (
<button
type="button"
onClick={() => {
api.change("nome", "John Doe");
api.change("email", "john@example.com");
api.change("cidade", "New York");
}}
>
Fill with my data
</button>
);
}
useFormState
Returns the reactive form state — re-renders on every field change.
import { useFormState } from "@schema-forms-data/renderer";
const { values, errors, warnings, dirty, valid, submitting } = useFormState();
Return type
interface FormStateValue {
values: Record<string, unknown>;
errors: Record<string, string | undefined>;
warnings: Record<string, string>; // from warn[] validators
dirty: boolean;
valid: boolean;
submitting: boolean;
}
Example
import { useFormState } from "@schema-forms-data/renderer";
function StatusBar() {
const { dirty, valid, submitting, errors } = useFormState();
return (
<div className="status-bar">
{dirty && <span className="badge-warning">Unsaved changes</span>}
{!valid && (
<span className="badge-error">
{Object.keys(errors).length} field(s) with errors
</span>
)}
{submitting && <span>Saving…</span>}
</div>
);
}
useField
Returns the state of a single field — reactive.
import { useField } from "@schema-forms-data/renderer";
const { value, error, warning, dirty, touched, valid } = useField("email");
Return type
interface FieldState {
value: unknown;
error?: string;
warning?: string; // from warn[] validators
dirty: boolean;
touched: boolean;
valid: boolean;
}
Example
import { useField } from "@schema-forms-data/renderer";
function EmailHint() {
const email = useField("email");
if (!email.touched) return null;
if (email.error) return <p className="error">{email.error}</p>;
if (email.warning) return <p className="warning">⚠ {email.warning}</p>;
return <p className="success">✓ Valid email</p>;
}
useFieldApi
Returns input props and meta data for building fully custom field components.
import { useFieldApi } from "@schema-forms-data/renderer";
const { input, meta } = useFieldApi("email");
Return type
interface FieldApiReturn {
input: {
name: string;
value: unknown;
onChange: (...args: unknown[]) => void;
onBlur: () => void;
ref: React.Ref<unknown>;
};
meta: {
error?: string;
warning?: string;
touched: boolean;
dirty: boolean;
valid: boolean;
};
}
Example
import { useFieldApi } from "@schema-forms-data/renderer";
function MyEmailInput() {
const { input, meta } = useFieldApi("email");
return (
<div>
<input
type="email"
{...(input as React.InputHTMLAttributes<HTMLInputElement>)}
className={meta.error ? "input-error" : ""}
/>
{meta.error && <span className="error">{meta.error}</span>}
{meta.warning && <span className="warning">{meta.warning}</span>}
</div>
);
}
<FormSpy>
Observe form state as a React component. Useful when you can't use hooks (e.g. you need to render outside the form, or bridge to a non-hook context).
import { FormSpy } from "@schema-forms-data/renderer";
Three usage patterns
1. Function-child (render-prop)
<FormSpy>
{({ values, dirty, valid, errors, warnings, submitting }) => (
<pre className="debug">{JSON.stringify(values, null, 2)}</pre>
)}
</FormSpy>
2. render prop
<FormSpy render={({ dirty }) => (dirty ? <SaveIndicator /> : null)} />
3. onChange callback (no UI output)
<FormSpy
onChange={(state) => {
// autosave, analytics, cross-step state sync...
if (state.dirty) {
sessionStorage.setItem("draft", JSON.stringify(state.values));
}
}}
/>
Props
interface FormSpyProps {
children?: ((state: FormStateValue) => React.ReactNode) | React.ReactNode;
render?: (state: FormStateValue) => React.ReactNode;
onChange?: (state: FormStateValue) => void;
}
Complete example
import { FormRenderer, FormSpy } from "@schema-forms-data/renderer";
function MyForm() {
return (
<FormRenderer
schema={schema}
onComplete={async (values) => api.save(values)}
>
<FormSpy
onChange={(state) => {
analytics.track("form_change", {
dirty: state.dirty,
valid: state.valid,
});
}}
/>
</FormRenderer>
);
}
onValuesChange prop
The simplest way to observe field changes from outside the renderer tree — pass onValuesChange directly to <FormRenderer>:
<FormRenderer
schema={schema}
onValuesChange={(values) => {
// called on every field change in the current step
sessionStorage.setItem("form-draft", JSON.stringify(values));
}}
onComplete={handleComplete}
/>
getState (snapshot)
Use useFormApi().getState() to read the form state synchronously on demand (not reactive):
import { useFormApi } from "@schema-forms-data/renderer";
function SaveButton() {
const api = useFormApi();
const handleSave = () => {
const state = api.getState();
if (state.valid) {
api.submit();
} else {
console.log("Errors:", state.errors);
}
};
return <button onClick={handleSave}>Save</button>;
}
interface FormStateSnapshot {
values: Record<string, unknown>;
errors: Record<string, string | undefined>;
warnings: Record<string, string>;
dirty: boolean;
valid: boolean;
submitting: boolean;
}
Todos os hooks e o <FormSpy> devem ser usados dentro da árvore do <FormRenderer> — por exemplo, dentro de um componente customizado via componentMapper, ou num componente filho passado via children do FormRenderer.
useFormApi
Retorna a API programática do formulário.
import { useFormApi } from "@schema-forms-data/renderer";
const api = useFormApi();
Tipo de retorno
interface FormApi {
change: (name: string, value: unknown) => void;
submit: () => void;
reset: (values?: Record<string, unknown>) => void;
getState: () => FormStateSnapshot;
}
Exemplo
import { useFormApi } from "@schema-forms-data/renderer";
function BotaoPreenchimento() {
const api = useFormApi();
return (
<button
type="button"
onClick={() => {
api.change("nome", "João Silva");
api.change("email", "joao@email.com");
api.change("cidade", "São Paulo");
}}
>
Preencher com meus dados
</button>
);
}
useFormState
Retorna o estado reativo do formulário — re-renderiza a cada mudança de campo.
import { useFormState } from "@schema-forms-data/renderer";
const { values, errors, warnings, dirty, valid, submitting } = useFormState();
Tipo de retorno
interface FormStateValue {
values: Record<string, unknown>;
errors: Record<string, string | undefined>;
warnings: Record<string, string>; // de validadores warn[]
dirty: boolean;
valid: boolean;
submitting: boolean;
}
Exemplo
import { useFormState } from "@schema-forms-data/renderer";
function BarraDeStatus() {
const { dirty, valid, submitting, errors } = useFormState();
return (
<div className="barra-status">
{dirty && <span className="badge-aviso">Alterações não salvas</span>}
{!valid && (
<span className="badge-erro">
{Object.keys(errors).length} campo(s) com erro
</span>
)}
{submitting && <span>Salvando…</span>}
</div>
);
}
useField
Retorna o estado de um único campo — reativo.
import { useField } from "@schema-forms-data/renderer";
const { value, error, warning, dirty, touched, valid } = useField("email");
Tipo de retorno
interface FieldState {
value: unknown;
error?: string;
warning?: string; // de validadores warn[]
dirty: boolean;
touched: boolean;
valid: boolean;
}
Exemplo
import { useField } from "@schema-forms-data/renderer";
function IndicadorCpf() {
const { value, error, touched } = useField("cpf");
if (!touched) return null;
return (
<div>
{error ? (
<span style={{ color: "red" }}>CPF inválido: {error}</span>
) : (
<span style={{ color: "green" }}>CPF válido: {String(value)}</span>
)}
</div>
);
}
useFieldApi
Retorna o input e o meta de um campo — útil para criar componentes de campo completamente customizados.
import { useFieldApi } from "@schema-forms-data/renderer";
const { input, meta } = useFieldApi("email");
Tipo de retorno
interface FieldApiReturn {
input: {
name: string;
value: unknown;
onChange: (value: unknown) => void;
onBlur: () => void;
ref: React.Ref<unknown>;
};
meta: {
error?: string;
warning?: string;
dirty: boolean;
touched: boolean;
valid: boolean;
};
}
FormSpy
Componente de render-prop para observar o estado do formulário.
import { FormSpy } from "@schema-forms-data/renderer";
// Autosave ao detectar mudanças
<FormSpy
onChange={(state) => {
sessionStorage.setItem("rascunho", JSON.stringify(state.values));
}}
>
{({ dirty, valid }) => (
<div className="rodape-formulario">
{dirty && <span>Você tem alterações não salvas</span>}
<button type="submit" disabled={!valid}>
Enviar
</button>
</div>
)}
</FormSpy>;
Props do FormSpy
| Prop | Tipo | Descrição |
|---|---|---|
onChange | (state: FormStateValue) => void | Callback quando o estado muda |
children | (state: FormStateValue) => React.ReactNode | Render prop que recebe o estado |
render | (state: FormStateValue) => React.ReactNode | Alternativa ao children |
### Example
```tsx
import { useField } from "@schema-forms-data/renderer";
function EmailHint() {
const email = useField("email");
if (!email.touched) return null;
if (email.error) return <p className="error">{email.error}</p>;
if (email.warning) return <p className="warning">⚠ {email.warning}</p>;
return <p className="success">✓ Valid email</p>;
}
useFieldApi
Returns input props and meta data for building custom field components.
import { useFieldApi } from "@schema-forms-data/renderer";
const { input, meta } = useFieldApi("email");
Return type
interface FieldApiReturn {
input: {
name: string;
value: unknown;
onChange: (...args: unknown[]) => void;
onBlur: () => void;
ref: React.Ref<unknown>;
};
meta: {
error?: string;
warning?: string;
touched: boolean;
dirty: boolean;
valid: boolean;
};
}
Example
import { useFieldApi } from "@schema-forms-data/renderer";
function MyEmailInput() {
const { input, meta } = useFieldApi("email");
return (
<div>
<input
type="email"
{...(input as React.InputHTMLAttributes<HTMLInputElement>)}
className={meta.error ? "input-error" : ""}
/>
{meta.error && <span className="error">{meta.error}</span>}
{meta.warning && <span className="warning">{meta.warning}</span>}
</div>
);
}
<FormSpy>
Observe form state as a React component. Useful when you can't use hooks (e.g. you need to render outside the form, or bridge to a non-hook context).
import { FormSpy } from "@schema-forms-data/renderer";
Three usage patterns
1. Function-child (render-prop)
<FormSpy>
{({ values, dirty, valid, errors, warnings, submitting }) => (
<pre className="debug">{JSON.stringify(values, null, 2)}</pre>
)}
</FormSpy>
2. render prop
<FormSpy render={({ dirty }) => (dirty ? <SaveIndicator /> : null)} />
3. onChange callback (no UI output)
<FormSpy
onChange={(state) => {
// autosave, analytics, cross-step state sync...
if (state.dirty) {
sessionStorage.setItem("draft", JSON.stringify(state.values));
}
}}
/>
Props
interface FormSpyProps {
children?: ((state: FormStateValue) => React.ReactNode) | React.ReactNode;
render?: (state: FormStateValue) => React.ReactNode;
onChange?: (state: FormStateValue) => void;
}
Complete example
import { FormRenderer, FormSpy } from "@schema-forms-data/renderer";
function MyForm() {
return (
<FormRenderer
schema={schema}
onComplete={async (values) => api.save(values)}
>
{/* FormSpy can be passed as children to FormRenderer in advanced setups,
or mounted inside a componentMapper component */}
<FormSpy
onChange={(state) => {
analytics.track("form_change", {
dirty: state.dirty,
valid: state.valid,
});
}}
/>
</FormRenderer>
);
}
onValuesChange prop
The simplest way to observe field changes from outside the renderer tree — pass onValuesChange directly to <FormRenderer>:
<FormRenderer
schema={schema}
onValuesChange={(values) => {
// called on every field change in the current step
sessionStorage.setItem("form-draft", JSON.stringify(values));
}}
onComplete={handleComplete}
/>
getState (snapshot)
Use useFormApi().getState() to read the form state synchronously on demand (not reactive):
import { useFormApi } from "@schema-forms-data/renderer";
function SaveButton() {
const api = useFormApi();
const handleSave = () => {
const state = api.getState();
if (state.valid) {
api.submit();
} else {
console.log("Errors:", state.errors);
}
};
return <button onClick={handleSave}>Save</button>;
}
interface FormStateSnapshot {
values: Record<string, unknown>;
errors: Record<string, string | undefined>;
warnings: Record<string, string>;
dirty: boolean;
valid: boolean;
submitting: boolean;
}