-
Notifications
You must be signed in to change notification settings - Fork 19
Frontend Form Management
Caseflow is an application built around forms. From intake to scheduling of hearings, nearly every part of Caseflow depends on the use of forms. As such, we try to make using them as easy as possible!
Instead of writing bespoke logic for managing forms and/or form fields for every use case, we can take advantage of libraries that can handle much of the boilerplate functionality for us. We have adopted the usage of React Hook Form as a form management library. It allows us to rapidly build forms and not have to worry about getting/setting all the individual field values, building out our own custom validation, and more — all using a lightweight standards-based implementation.
By passing register
into the inputRef
prop on our form fields, we allow the usage of uncontrolled components (eliminating the need to set value
and onChange
on each field), but still maintain the overall form state. We pass handleSubmit
(returned from useForm
) to the onSubmit
prop of the form, allowing it to intercept the submission and perform validation, etc before passing the full data to our specified callback function.
By default, useForm
runs validation, etc on form submission. However, it supports specifying these modes: onSubmit
, onBlur
, onChange
, onTouched
, or all
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import TextField from '../../components/TextField';
import Button from '../../components/Button';
export const MyComponent = () => {
const { register, handleSubmit } = useForm();
const onSubmit = ({veteranName}) => {
// If we've gottten to this point, the form is valid and has been submitted.
// This callback could be defined in your component, passed in props, etc
// The argument is an object with keys of the various field names
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextareaField
inputRef={register}
name="veteranName"
label="Veteran Name"
strongLabel
/>
<Button type="submit">Submit</Button>
</form>
)
}
Using the form management library becomes particularly useful when coupled with a validation schema. Yup is a library that allows us to build validation schemata, and it integrates really easily with react-hook-form
.
Note that validation for React Hook forms is async and expectations during testing need to be wrapped in a waitFor
(example)
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers';
import * as yup from 'yup';
import TextField from '../../components/TextField';
import Button from '../../components/Button';
const TIMELY_OPTIONS = [
{ displayText: 'Yes', value: 'yes' },
{ displayText: 'No', value: 'no' },
];
const schema = yup.object().shape({
veteranName: yup.string().required(),
timely: yup.string().oneOf(['yes', 'no']),
});
export const MyComponent = () => {
const { register, handleSubmit, formState, errors } = useForm({
resolver: yupResolver(schema),
mode: 'onChange',
});
const onSubmit = (formData) => console.log('success!', formData);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextareaField
inputRef={register}
name="veteranName"
label="Veteran Name"
strongLabel
/>
<RadioField
name="timely"
label="Is this request timely?"
options={TIMELY_OPTIONS}
inputRef={register}
strongLabel
vertical
/>
<Button type="submit" disabled={!formState.isValid}>Submit</Button>
</form>
)
}
You can see these patterns utilized in the RecommendDocketSwitchForm
component