@formwire/react
v1.0.0
Published
React components and hooks for FormWire
Maintainers
Readme
@formwire/react
React components and hooks for FormWire - Ultra-lightweight form state management.
Features
- React Hooks - Modern React patterns
- High Performance - Optimized with selective subscriptions and memoization
- TypeScript Ready - Full type support
- Zero Dependencies - No external dependencies (except React)
- Memory Efficient - Automatic cleanup with WeakMap
Installation
npm install @formwire/reactQuick Start
import { Form, Field } from '@formwire/react';
function MyForm() {
return (
<Form onSubmit={(values) => console.log(values)}>
<Field
name="email"
validate={(value) => {
if (!value) return 'Email is required';
if (!value.includes('@')) return 'Invalid email';
return undefined;
}}
>
{({ input, meta }) => (
<div>
<input {...input} placeholder="Email" />
{meta.error && <span style={{ color: 'red' }}>{meta.error}</span>}
</div>
)}
</Field>
<button type="submit">Submit</button>
</Form>
);
}Components
Form
Root form component with context provider.
<Form
onSubmit={(values) => void}
initialValues={object}
defaultValidateOn="blur" | "change"
engine={FormEngine}
>
{children}
</Form>Props:
onSubmit(function): Submit handlerinitialValues(object): Initial form valuesdefaultValidateOn(string): Default validation mode ('blur' or 'change')engine(FormEngine): External engine instance (optional)
Field
High-performance field component with optimized subscriptions and validation.
<Field
name="fieldName"
validate={(value, allValues) => string | undefined}
validateOn="blur" | "change"
debounceDelay={number}
subscription={object}
>
{({ input, meta }) => JSX}
</Field>Props:
name(string): Field namevalidate(function): Validator functionvalidateOn(string): Validation mode ('blur' or 'change')debounceDelay(number): Debouncing delay in millisecondssubscription(object): Selective subscriptions
Render Props:
input: Field input props ({ name, value, onChange, onBlur, onFocus })meta: Field metadata ({ error, touched, active, dirty })
FieldArray
Component for working with field arrays.
<FieldArray name="items">
{({ fields, push, pop, remove, move, insert }) => (
<div>
{fields.map((field, index) => (
<div key={field.__id}>
<Field name={`${field.name}.title`}>
{({ input }) => <input {...input} />}
</Field>
<button onClick={() => remove(index)}>Remove</button>
</div>
))}
<button onClick={() => push({ title: '' })}>Add Item</button>
</div>
)}
</FieldArray>Props:
name(string): Array field name
Render Props:
fields: Array of field objects with stable IDspush: Add item to endpop: Remove last itemremove: Remove item by indexmove: Move item from one index to anotherinsert: Insert item at specific index
Hooks
useField
Hook for managing field state.
function MyField({ name }) {
const { value, onChange, onBlur, error, touched } = useField(name, {
validate: (value) => !value ? 'Required' : undefined,
validateOn: 'blur'
});
return (
<div>
<input
value={value}
onChange={onChange}
onBlur={onBlur}
/>
{touched && error && <span>{error}</span>}
</div>
);
}Parameters:
name(string): Field nameoptions(object): Field optionsvalidate(function): Validator functionvalidateOn(string): Validation modedebounceDelay(number): Debouncing delaysubscription(object): Selective subscriptions
Returns:
value: Field valueonChange: Change handleronBlur: Blur handleronFocus: Focus handlererror: Validation errortouched: Touched statusactive: Active statusinput: Input props objectmeta: Metadata object
useFormState
Hook for subscribing to form state.
function FormStatus() {
const { values, errors, submitting, valid } = useFormState({
values: true,
errors: true,
submitting: true
});
return (
<div>
<p>Valid: {valid ? 'Yes' : 'No'}</p>
<p>Submitting: {submitting ? 'Yes' : 'No'}</p>
<p>Values: {JSON.stringify(values)}</p>
</div>
);
}Parameters:
subscription(object): Selective subscriptionsvalues(boolean): Subscribe to valueserrors(boolean): Subscribe to errorstouched(boolean): Subscribe to touchedactive(boolean): Subscribe to activesubmitting(boolean): Subscribe to submitting
Returns:
values: Form valueserrors: Validation errorstouched: Touched fieldsactive: Active fieldsubmitting: Submission statusvalid: Form validitydirty: Dirty statuspristine: Pristine status
useWatch
Hook for watching specific field.
function EmailWatcher() {
const email = useWatch('email', (value) => value?.toLowerCase());
return <p>Email: {email}</p>;
}Parameters:
name(string): Field nameselector(function): Selector for transformation
Returns:
- Field value (transformed by selector)
useFormSubmit
Hook for handling form submission.
function SubmitButton() {
const { handleSubmit, submitting } = useFormSubmit(async (values) => {
await submitToAPI(values);
});
return (
<button onClick={handleSubmit} disabled={submitting}>
{submitting ? 'Submitting...' : 'Submit'}
</button>
);
}Parameters:
onSubmit(function): Submit handler
Returns:
handleSubmit: Submit handlersubmitting: Submission status
Context
FormProvider
Context provider for form engine.
import { FormProvider } from '@formwire/react';
function App() {
return (
<FormProvider engine={customEngine}>
<MyForm />
</FormProvider>
);
}useFormEngine
Hook for accessing form engine.
function CustomComponent() {
const engine = useFormEngine();
// Use engine directly
engine.set('customField', 'value');
}useFormContext
Hook for accessing form context.
function CustomComponent() {
const context = useFormContext();
// Access context properties
console.log(context.engine);
}Advanced Usage
Custom Field Component
function CustomField({ name, label, ...props }) {
return (
<Field name={name} {...props}>
{({ input, meta }) => (
<div className="field">
<label>{label}</label>
<input {...input} className={meta.error ? 'error' : ''} />
{meta.touched && meta.error && (
<div className="error-message">{meta.error}</div>
)}
</div>
)}
</Field>
);
}Selective Subscriptions
function OptimizedField({ name }) {
const { value, onChange } = useField(name, {
subscription: {
value: true,
onChange: true
// Don't subscribe to error, touched, etc.
}
});
return <input value={value} onChange={onChange} />;
}Custom Validation
function EmailField() {
return (
<Field
name="email"
validate={async (value) => {
if (!value) return 'Email is required';
if (!value.includes('@')) return 'Invalid email format';
// Async validation
const exists = await checkEmailExists(value);
if (exists) return 'Email already exists';
return undefined;
}}
>
{({ input, meta }) => (
<div>
<input {...input} type="email" />
{meta.error && <span>{meta.error}</span>}
</div>
)}
</Field>
);
}Performance Tips
- Use selective subscriptions to minimize re-renders
- Memoize expensive computations with useMemo
- Use debounced validation for better performance
- Avoid unnecessary subscriptions in useFormState
License
MIT
