A headless React form component. It allows you to specify form fields, which will then create child form controllers with separate states. All form controls will update in their own independent states, allow the field to update just itself instead of all fields.
Being headless means that it does not create any form components itself and works with any forms you already have.
npm install --save @rageix/former
See the /examples directory.
Create a new instance of the Former class and define the initial form.
import Former from '@rageix/former';
// define our form
interface SignUpForm {
email: string;
password: string;
passwordAgain: string;
count: number,
terms: boolean;
}
// create a new instances of the Former class
const form = new Former<SignUpForm>({
defaultValues: {
email: '',
password: '',
passwordAgain: '',
count: 10,
terms: true,
},
});
Keep in mind that form fields can be strings, numbers, and booleans. When the form field changes it will automatically be cast into the same type.
In your form call the useForm() method which will call the proper useState functions
for all the child components. This useForm() method takes a callback function that fires
when the form submits.
function App() {
form.useForm((value: SignUpForm) => {
// do submit logic here
console.log(value);
});
Now you want to setup your form and attach the proper handlers to the form element.
<form {...form.getFormProps()}>
{/* children here */}
</form>
The getformProps() method will set the onSubmit handler to the internal Former functions.
This is optional if you do not want to use the onSubmit handler.
Next up add your form fields here is a simple example:
<form.Field
name="email"
validate={(value) => {
const result = z.string().email().safeParse(value);
if (!result.success) {
return result.error.issues.map((v) => v.message);
}
}}
// this is just an example
transformValue={(value) => value}
children={(field) => (
<div>
<label htmlFor="email">Email</label>
<input
name="email"
data-testid="email"
{...field.getValueProps()}
/>
<FormErrors errors={field.state.errors} />
</div>
)}
/>
Let's talk about this from top to bottom.
The name prop is the name of the field you used in defaultValues when creating a
new instance of Former.
The validate props is a function used to validate the field. It returns a string[] of
errors.
The transformValue prop is used to transform the input value. The user enters a value,
the onChange handler is called, Former converts it into the type that was set in
defaultValues, then the transformValue function is called if it is set. This method
takes an argument of the value, and you can return whatever value you want and that is the
value that gets set.
The children props is the standard React child prop. However, a controller for the
field is passed as the first argument. This controller contains everything about the
field.
The field.getValueProps() method returns the value and onChange handlers for the
component. This is the method to use when your component needs a value prop passed.
There is a field.getCheckedProps() for when the filed needs a checked prop.
To access the field errors you can do so though field.state.errors which is a string[].
Lastly is some kind of submit button for the form, and you have 2 options.
- If you used the
getFormProps()method on your form element you just need a basic submit button.
<button
type="submit"
disabled={!form.valid}
>
Submit
</button>
- If you want the form button to trigger the submit, or handle it in another way you can add
the
form.getSubmitButtonProps()method to any component and it will add the proper click handler to whatever component it's on. You could also directly call theform.submit()method if you wanted to manually trigger a submit.
form.getValue(name) - Allows you to get the value of any field by it's name.
form.getValues() - Allows you to get all the values in the form.
form.setValues(values) - Allows you to pass in a Partial of your form data to set any fields.