[Status]: Experimental / Alpha
A simple React form hook.
- Works with standard react inputs - no custom components needed
- Should work with any validation library
yarn add usable-form
or
npm install usable-form
import * as React from 'react';
import { useForm } from 'usable-form';
function Form() {
const { setupField, formValues } = useForm({
initialValues: {
firstName: 'Joe'
}
});
return (
<form>
<input {...setupField('firstName')} />
<button
type="submit"
onSubmit={() => {
console.log(formValues); // { firstName: 'Joe' }
}}
/>
</form>
);
}
import * as React from 'react';
import { useForm } from 'usable-form';
function Form() {
const { setupField, formValues } = useForm({
initialValues: {
firstName: 'Joe',
bankAccount: {
number: '0123456789'
}
}
});
return (
<form>
<input {...setupField('firstName')} />
<input {...setupField('bankAccount.number')} />
<button
type="submit"
onSubmit={() => {
console.log(formValues); // { firstName: 'Joe', bankAccount: { number: '0123456789' } }
}}
/>
</form>
);
}
import * as React from 'react';
import { useForm } from 'usable-form';
function Form() {
const { setupField, formState } = useForm({
initialValues: {
firstName: 'Joe'
},
/** validation function
*
* @param values - actual form values
* @param fieldName - (optional) field which triggered validation
* @return Array<ValidationError>
*/
validateForm: (values, fieldName) => {
const errors = [];
if (values.firstName !== 'Joe') {
errors.push({
source: 'firstName',
code: 'only-joe-allowed',
severity: 'error' // ('error' | 'warning')
});
}
return errors;
}
});
return (
<form>
<input {...setupField('firstName')} />
<button
type="submit"
onSubmit={() => {
console.log(formState); // { isValid: false, errors: [{ source: 'firstName', code: 'only-joe-allowed' ... } ...] }
}}
/>
</form>
);
}
import * as React from 'react';
import { useForm, initUseForm } from 'usable-form';
// Initialize once on application start-up
initUseForm({
/** If defined, this function could adapt to
* to a schema validation library of your choice.
*
* @param schema - a schema object which your validation library understands
* @param values - an actual form values
* @param fieldName - (optional) field which triggered validation
* @return Array<ValidationError> - empty if valid
*/
schemaValidator: (schema, values, fieldName) => {
// TODO: execute validation and return validation errors for the current form values
return [];
}
});
function Form() {
const { setupField, formState } = useForm({
initialValues: {
firstName: 'Joe'
},
validationSchema: { firstName: { required: true } }
});
return (
<form>
<input {...setupField('firstName')} />
<button
type="submit"
onSubmit={() => {
console.log(formState);
}}
/>
</form>
);
}
useForm hook keeps information about actual form values and form state.
type ValidationError = {
source: string,
code: string,
message?: string,
severity: 'error' | 'warning' | string
};
type UseFormOptions = {
initialValues?: FormValues,
validationSchema?: Object,
validateForm?: (
formValues: FormValues,
fieldName: ?string
) => Array<ValidationError>
};
type UseFormResult = {
setupField: (fieldName: string) => SetupFieldResult,
setupWrapper: (fieldName: string, locPrefix?: string) => SetupWrapperResult,
setFieldValue: (fieldName: string, value: any) => void,
formValues: FormValues,
formState: FormState,
setValues: (values: Object) => void
};
const useFormOptions: UseFormOptions = {};
const useFormResult: UseFormResult = useForm(useFormOptions);
A simple helper function which will return important props to bind an React input field. It will provide a default implementation for these props:
- name
- id
- value
- onChange
- onBlur
You can override any of these props or even bind them all manually
function Form() {
const { setupField } = useForm();
return (
<form>
<input {...setupField('firstName')} />
</form>
);
}
Actual form values.
function Form() {
const { formValues } = useForm();
.
.
.
}
Form meta data. Holds information about validation state and touched fields
type FormState = {
touched: Array<string>,
errors: Array<ValidationError>,
isValid: boolean,
setFormErrors: (errors: Array<ValidationError>) => void
};
function Form() {
const { formState } = useForm();
}
Sets form value imperatively
function Form() {
const { setupField, setFieldValue } = useForm();
return (
<form>
<input {...setupField('firstName')} />
<button onClick={() => setFieldValue('firstName', 'Elvis')} />
</form>
);
}
Resets form value imperatively. It will replace all form values with the new object!
function Form() {
const { setupField, setValues } = useForm();
return (
<form>
<input {...setupField('firstName')} />
<button onClick={() => setValues({ firstName: 'Elvis' })} />
</form>
);
}
If you create a component which will provide at least these props:
type WrapperProps = {
error: ?ValidationError,
touched: boolean,
children: Node
};
then you can utilize a setupWrapper
helper function
which will bind a label, field error and field touch status
for you:
// Simple InputWrapper
function InputWrapper(props) {
const { error, touched, label } = props;
return (
<div>
<div className="label">{label}</div>
<div>{props.children}</div>
{error && touched && <div className="error">{error}</div>}
</div>
);
}
// Form utilizing InputWrapper
function Form() {
const { setupField, setupWrapper } = useForm();
return (
<form>
<InputWrapper {...setupWrapper('firstName')}>
<input {...setupField('firstName')} />
</InputWrapper>
</form>
);
}