useGioFormState
Read the state of the enclosing <GioForm> - whether a submission is pending and how the last one ended - from any component inside it.
app/components/submit-button.tsx
import type { ReactNode } from 'react';
import { useGioFormState } from '@gio.js/react';
export function SubmitButton({ children }: { children: ReactNode }) {
const { pending } = useGioFormState();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving...' : children}
</button>
);
}Reference
Parameters
useGioFormState takes no parameters.
Returns
A GioFormState:
| Field | Type | Default | Description |
|---|---|---|---|
pending | boolean | - | true from the moment the form submits until the answer is on screen. |
lastResult | GioFormResult | null | - | How the last finished submission ended - ok, status, url, redirected, data, response, error (see GioFormResult). null before the first. |
Behavior
- It reads the nearest
<GioForm>above the component. Outside one it always returns{ pending: false, lastResult: null }. pendingstaystruewhile the browser loads a page the router cannot render (a redirect to another site), so a button stays disabled until the page goes away; it ends if the back/forward cache brings the page back, and for a file download.- A submission overtaken by a newer navigation ends
pendingand keeps the previouslastResult. - Before hydration, and without JavaScript, the form posts natively and the state stays idle.
Examples
A status line
app/settings/save-status.tsx
import { useGioFormState } from '@gio.js/react';
export function SaveStatus() {
const { pending, lastResult } = useGioFormState();
if (pending) return <p aria-live="polite">Saving...</p>;
if (lastResult === null) return null;
if (lastResult.status === 0) return <p role="alert">Network error - your changes are still here.</p>;
if (!lastResult.ok) return <p role="alert">Could not save (HTTP {lastResult.status}).</p>;
return <p aria-live="polite">Saved.</p>;
}app/settings/page.tsx
import type { ActionArgs } from '@gio.js/core';
import { GioForm } from '@gio.js/react';
import { SaveStatus } from './save-status.tsx';
import { SubmitButton } from '../components/submit-button.tsx';
export async function action(req: ActionArgs) {
const form = await req.formData();
await settings.save({ theme: String(form.get('theme')) });
return { saved: true };
}
export default function Settings() {
return (
<GioForm>
<select name="theme">
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<SubmitButton>Save</SubmitButton>
<SaveStatus />
</GioForm>
);
}Without a separate component
The hook needs a component inside the form. For a one-off, pass a function as the form's children; it receives the same state.
tsx
<GioForm>
{({ pending }) => <button disabled={pending}>{pending ? 'Sending...' : 'Send'}</button>}
</GioForm>Good to know
- Called in the component that renders the
<GioForm>, it reads the form around that component, not this one - which is usually none, so it is idle. - The form itself also exposes
pendingasaria-busy="true", for CSS:form[aria-busy="true"] { opacity: 0.6 }. - While
pendingis true, further submits of that form are dropped, whether or not you disable the button.
Related
<GioForm>- the form and its callbacks.- Forms and Mutations - the guide.
action- the page export a form posts to.
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | Introduced. |