logic-form
v0.2.1
Published
State management for React
Readme
logic-form
Lightweight form state management for React. Provides hooks for form-level and field-level validation with a parent-child tree model.
Install
npm install logic-formReact 17, 18, or 19 is required as a peer dependency.
Quick start
import { useForm, useFormField, BindFormField, RenderFormErrors } from 'logic-form'
function LoginForm() {
const form = useForm()
const email = useFormField(form, {
name: 'email',
value: emailValue,
validators: [
(v) => { if (!v) return 'Email is required' },
(v) => { if (!v.includes('@')) return 'Invalid email' },
],
})
const password = useFormField(form, {
name: 'password',
value: passwordValue,
validators: [
(v) => { if (!v) return 'Password is required' },
],
})
const onSubmit = form.handleSubmit(() => {
// Only called when all fields are valid
login(emailValue, passwordValue)
})
return (
<form onSubmit={(e) => { e.preventDefault(); onSubmit() }}>
<BindFormField field={email}>
<input type="email" onChange={(e) => setEmailValue(e.target.value)} />
</BindFormField>
<RenderFormErrors of={email} />
<BindFormField field={password}>
<input type="password" onChange={(e) => setPasswordValue(e.target.value)} />
</BindFormField>
<RenderFormErrors of={password} />
<button type="submit">Log in</button>
</form>
)
}API
useForm(config?)
Creates a form instance. Returns a LogicForm object.
const form = useForm()
const form = useForm({ name: 'myForm', submitOnPressEnter: false })Config options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| name | string | undefined | Optional form name |
| submitOnPressEnter | boolean | true | Submit form when Enter is pressed in a field |
| errorRenderer | (msg: string) => any | built-in | Custom error rendering function |
Returned form object:
| Property / Method | Description |
|-------------------|-------------|
| children | Array of registered fields and sub-forms |
| shouldRunValidations | Whether validation has been triggered |
| handleSubmit(fn) | Wraps a submit handler; calls fn only if hasErrors() is false |
| submit() | Calls the wrapped submit handler |
| runValidations() | Triggers validation on all fields |
| resetValidation() | Resets validation state on the form and all children |
| hasErrors() | Returns true if any child has errors |
| renderErrors() | Collects error output from all children |
useForm(parentForm, config?)
Creates a sub-form nested under a parent form. The sub-form registers itself as a child.
const parent = useForm({ name: 'parent' })
const shipping = useForm(parent, { name: 'shipping' })useFormField(form, config)
Creates a field attached to a form. Returns a LogicFormField object.
const field = useFormField(form, {
name: 'email',
value: currentValue,
validators: [(v) => { if (!v) return 'Required' }],
})Config options:
| Option | Type | Description |
|--------|------|-------------|
| name | string | Field name (required) |
| value | any | Current field value (required) |
| validators | ((value, name) => string \| void)[] | Array of validator functions |
| errorMessage | string | Explicit error message (bypasses validators) |
| errorRenderer | (msg: string) => any | Custom error rendering for this field |
Validators receive the field value and name. Return a string to indicate an error, or undefined/void to pass. Validators run in order and stop at the first error.
Returned field object:
| Property / Method | Description |
|-------------------|-------------|
| value | Current field value |
| name | Field name |
| isValid | true if no error message |
| errorMessage | Current error message or undefined |
| handleChange(cb?, delay?) | Returns an event handler that calls cb and schedules validation |
| handleKeyDown(cb?) | Returns a keydown handler; submits form on Enter |
| resetValidation() | Resets the field's changed state |
| hasErrors() | Returns true if the field has errors |
| renderErrors() | Renders the error using the nearest error renderer |
| runValidators() | Manually runs validators and returns the error message |
BindFormField
Component that binds a field to a child input element, injecting value, onChange, onBlur, and onKeyDown props.
<BindFormField field={email} validationDelay={{ onChange: 300 }}>
<input type="text" />
</BindFormField>Props:
| Prop | Type | Description |
|------|------|-------------|
| field | LogicFormField | The field to bind (required) |
| children | JSX.Element | A single input element (required) |
| validationDelay | { onChange?: number, onBlur?: number } | Delay in ms before validation triggers |
RenderFormErrors
Component that renders a field's error when it is invalid. Returns null when the field is valid.
<RenderFormErrors of={email} />Validation behavior
- Validators do not run until the field is changed (via
handleChange) orform.runValidations()is called. form.handleSubmit(fn)checkshasErrors()before callingfn. If errors exist, it triggersrunValidations()so all error messages appear.hasErrors()always checks for errors regardless of whether the field has been touched.resetValidation()on the form resets all children to their untouched state.
License
ISC
