useFormState
The useFormState hook returns the current form state without providing setters.
Import
import { useFormState } from 'yet-another-form/react'Usage
Must be used within a <Form> component or with manually passed form context:
// Inside Form component
function SubmitButton() {
const { isDirty, isValid, isSubmitting } = useFormState()
return (
<button disabled={!isDirty || !isValid || isSubmitting}>
{isSubmitting ? 'Saving...' : 'Save'}
</button>
)
}
// With manual context
const { formContext } = useForm()
const formState = useFormState(formContext)Arguments
formContext
- Type:
FormContext - Optional
Form context for using the hook outside the <Form> component.
const { formContext } = useForm()
const state = useFormState(formContext)Return Value
isDirty
- Type:
boolean
Whether any field has been changed from its initial value.
const { isDirty } = useFormState()
console.log(isDirty) // true/falseisSubmitting
- Type:
boolean
Whether the form is currently submitting.
const { isSubmitting } = useFormState()
{
isSubmitting && <Spinner />
}isValid
- Type:
boolean
Whether the form has no errors.
const { isValid } = useFormState()
console.log(isValid) // true/falsedirtyFields
- Type:
string[] - Auto-subscribable
Array of field names that have been changed.
const { dirtyFields } = useFormState()
console.log(dirtyFields) // ['name', 'email']Auto-Subscribable
If you don't read this property, your component won't re-render when it changes.
errorFields
- Type:
string[] - Auto-subscribable
Array of field names that have errors.
const { errorFields } = useFormState()
console.log(errorFields) // ['email']Auto-Subscribable
If you don't read this property, your component won't re-render when it changes.
validatingFields
- Type:
string[] - Auto-subscribable
Array of field names currently being validated (async validation).
const { validatingFields } = useFormState()
{
validatingFields.length > 0 && <Spinner />
}Auto-Subscribable
If you don't read this property, your component won't re-render when it changes.
Examples
Submit Button
function SubmitButton() {
const { isDirty, isValid, isSubmitting } = useFormState()
return (
<button type="submit" disabled={!isDirty || !isValid || isSubmitting}>
{isSubmitting ? 'Saving...' : 'Save Changes'}
</button>
)
}Form Status Display
function FormStatus() {
const state = useFormState()
return (
<div className="form-status">
{state.isDirty && <span>You have unsaved changes</span>}
{state.errorFields.length > 0 && (
<span>Please fix {state.errorFields.length} error(s)</span>
)}
{state.validatingFields.length > 0 && (
<span>Validating {state.validatingFields.join(', ')}...</span>
)}
</div>
)
}Conditional Rendering
function FormActions() {
const { isDirty, isValid } = useFormState()
if (!isDirty) {
return <p>No changes to save</p>
}
return (
<div>
<button type="submit" disabled={!isValid}>
Save
</button>
<button type="reset">Cancel</button>
</div>
)
}Progress Indicator
function FormProgress() {
const { errorFields, validatingFields } = useFormState()
return (
<div>
{validatingFields.length > 0 && (
<div className="validating">
Validating: {validatingFields.join(', ')}
</div>
)}
{errorFields.length > 0 && (
<div className="errors">Errors in: {errorFields.join(', ')}</div>
)}
</div>
)
}Use Case
The useFormState hook is useful when:
- You only need to read form state without modifying it
- You want to create separate components for form controls
- You need to display form status or progress
- You want to conditionally render based on form state
Comparison with useForm
| Feature | useForm | useFormState |
|---|---|---|
| Returns setters | ✅ | ❌ |
| Returns Form component | ✅ | ❌ |
| Returns state | ✅ | ✅ |
| Can be used in Form children | ❌ | ✅ |
| Requires form context | ❌ (creates it) | ✅ (must be inside Form) |
TypeScript
interface FormState {
isDirty: boolean
isSubmitting: boolean
isValid: boolean
dirtyFields: string[]
errorFields: string[]
validatingFields: string[]
}
function useFormState(formContext?: FormContext): FormState