There is generally a high learning curve when working with a new library or framework and trying to implement forms. With data-bindings, validation, and UI, it’s not a simple task creating a new form. Our team started developing a React application last year with the plan of adding over a dozen forms. We knew React Hooks was coming and that it would make lifecycle methods and state easier to manage, but that wasn’t going to prevent us from moving forward. When our team was preparing to implement forms, we knew we needed a solid foundation. We didn’t want to be dependent on a framework and we wanted to be able to write our own reusable form components.
I started investigating React form solutions and began by following the documentation the React team provides. The examples were very basic and provided just a small piece of the solution.
class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''};
this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); }
handleChange(event) { this.setState({value: event.target.value}); }
handleSubmit(event) { alert('A name was submitted: ' + this.state.value); event.preventDefault(); }
render() { return ( <form onSubmit={this.handleSubmit}> <label> Name: <input type="text" value={this.state.value} onChange={this.handleChange} /> </label> <input type="submit" value="Submit" /> </form> ); }}
This is the basic example that React provided. It seemed easy enough to get started, but I really didn’t like how much boilerplate code was required to create just this single form. Imagine how this file would look if you had 10 form fields with custom onChange handlers. In addition to having to bind the onChange and onSubmit methods, I would need to introduce a validation solution and know if a field was touched or contained an error.
Do I need a class component just to set up default form values in state?
After weighing the do-it-yourself approach and investigating a few smaller form libraries, I decided it was time to start looking at other packages that could help with creating a complete form solution and validation schemas.
Formik and Redux forms seemed to be the most popular libraries that teams are using now. Our team hadn’t introduced Redux yet so I decided to skip it for now. It also didn’t seem like a best practice to keep form values in a global state management solution in most cases.
Form values are generally stored in local state not global state.
Formik is recommended by the React team as a complete solution and can handle validation, input bindings, as well as errors and state changes. With this recommendation, I set out to learn more and create some test examples to bring to my team.
Formik’s website claims to help you with the most annoying issues of forms:
After creating a few forms myself using Formik, I can see how these common issues just disappear. Form values are not stored in state so we don’t need to manually bind them to an input. Instead, Formik keys off the name attribute and does the binding automatically.
There are two ways to use Formik: create a higher order component or create a Formik component to wrap around your form.
When you wrap your form in a Formik component, the child is a function and not a component. This function takes one argument which is a variety of props Formik passes you to manage state. This helps reduce the boilerplate code involved in creating a form by providing the following default props: handleSubmit, handleChange, initialValues, validation and more.
Using Formik has allowed us to create our own reusable form components which were a large factor in our decision to use the library. It makes creating new forms painless and allows us to test them easily.
import { Formik } from "formik";
<FormikinitialValues={this.state.InitialFormValues}onSubmit={(values, { setSubmitting }) => {// handle submit}}validationSchema={loginValidation}
{props => {const {values,touched,errors,dirty,isSubmitting,handleChange,handleBlur,handleSubmit,handleReset} = props;
return (<form onSubmit={handleSubmit}></form>
)</Formik>
React dev tools <Formik/> Component
Formik is a controlled input solution which means the form values are handled by the components and not captured from the DOM at a later time. Check out the documentation from the React team on controlled and uncontrolled forms.
Formik · Build forms in React, without tears._Build forms in React, without the tears._jaredpalmer.com
An equally important part of any form solution is validation. Formik allows you to choose or write your own validation and it’s very easy to get started.
The Formik team loves the Yup validation library so they created a specific prop for Yup called validationSchema which transforms errors into objects and matches against their values and touched functions.
A Yup schema is an immutable object responsible for validating an object.
const signupValidationSchema = Yup.object().shape({FirstName: Yup.string().max(40, 'Please enter no more than 40 characters').required( 'Please enter your first name' ),LastName: Yup.string().max(40, 'Please enter no more than 40 characters').required('Please enter a last name'),Email: Yup.string().email('Please enter a valid email').required('Please enter an email')});
The validate prop gives you access to values of controlled inputs and runs on every onChange. Yup then returns an error object with all our validation messages. Below is the code that would be duplicated for each input.
{errors.firstName && touched.firstName ? (<div>{errors.firstName}</div>) : null}
Luckily Formik provides a Component that will display an error if the name attribute matches the input. This is much cleaner and again, saves us from re-writing conditionals for each form field.
<Inputtype="text"name="FirstName"placeholder={'Please enter a name'}onChange={handleChange}onBlur={handleBlur}value={values.FirstName}/>
<ErrorMessage name="FirstName">{msg => <div className="error error-message">{msg}</div>}</ErrorMessage>
Our team created a separate Yup validation schema and then imported it into our form.
<Formik validationSchema={accountSettingValidation} />
jquense/yup_Dead simple Object schema validation. Contribute to jquense/yup development by creating an account on GitHub._github.com
Formik is a great solution to try if your team will be working with multiple forms. It allows for quick development as well as the freedom to create your own form components. This library is trusted by the community and currently has over 250,000 weekly downloads.
Yup validation works really well with Formik and offers extensive API documentation.
If you have had issues in the past with forms or are just starting out I would highly recommend looking at these options. Reduce that amount of boilerplate code and make forms fun again!