@identityofsine/radioactive-forms
v2.2.4
Published
Powerful forms for React
Maintainers
Readme
Radioactive Forms
Type-safe, reactive forms for React.
Overview
- Typed controls with validators
- Nested forms and arrays of forms/controls
- Reactive updates via a lightweight hook
- Readonly/disabled modes and dirty/touched tracking
- Build fully typed output objects with
form.build()
Installation
npm i @identityofsine/radioactive-formsQuickstart
import { useForm, Validators, Form } from '@identityofsine/radioactive-forms';
type Profile = { name: string; email: string; age: number; agree: boolean };
export function Example() {
const { form } = useForm<Profile>({
name: ['', [Validators.required]],
email: ['', [Validators.required]],
age: [18, []],
agree: [false, []],
});
const controls = (form as unknown as { controls: Record<string, any> })?.controls;
return (
<>
<input value={String(controls?.name.value ?? '')} onChange={e => controls?.name && (controls.name.value = e.target.value)} />
<button disabled={!form?.valid || form?.readonly} onClick={() => console.log(Form.isForm(form) ? form.build() : undefined)}>Submit</button>
</>
);
}Core Concepts
- Form template:
{ field: [initialValue, [validators...]] } Formstate:valid,dirty,touched,readonly,disabledFormmethods:reset(),patchValue(partial),build(); staticForm.isForm(obj)FormControl<T, O>:.valuesetter triggers validation,.reset(),.patchValue()- Validators:
Validators.required - Nesting: controls can hold nested
Formor arrays ofForm/FormControl
API Surface
useForm<T>(template, options?, deps?) => { form?: Form<T> }Form<T>:.controls,.invalids,.getControl(key),.addControls(map),.readonly,.disabled,.reset(),.patchValue(),.build()FormControl<T, O>:.value,.readonly,.disabled,.reset(),.patchValue()Validators:required- React context:
FormGroupfor providingformvia context - React hook:
useFormGroup<T>(options?) => { form?: Form<T> }
useFormGroup Example
import { FormGroupProvider, useFormGroup, useForm } from '@identityofsine/radioactive-forms';
type Profile = { name: string; email: string };
function Child() {
const { form } = useFormGroup<Profile>({ required: true });
const controls = (form as any)?.controls;
return (
<input value={String(controls?.name.value ?? '')} onChange={e => controls?.name && (controls.name.value = e.target.value)} />
);
}
export function Parent() {
const { form } = useForm<Profile>({ name: ['', []], email: ['', []] });
return (
<FormGroupProvider form={form}>
<Child />
</FormGroupProvider>
);
}Examples / Local Development
The example app (development environment) lives in example/basic-react-forms and hot-reloads against local sources.
npm run example:install
npm run example:devThis runs Vite in example/basic-react-forms, which is configured to use the local src/index.ts so you can develop the library and see changes live.
Testing & Type-Checking
npm run test
npm run test:watch
npm run test:coverage
npm run typecheckContributing
PRs welcome! Use the example app for local development and run tests and type checks before submitting.
License
MIT
