Easy forms in React Native with Redux-Form
The key features implemented with redux-form
For those coming from an Angular background, developing forms in React may take longer than before, and even more so in React Native.
ngFormController is, in my opinion, one of the best parts of Angular and here is why:
- Sync validations are easy to accomplish using built-in directives (ng-min, ng-max length, ng-pattern, etc) even if you are developing your own validators.
- Async validations can be created with almost no overhead using directives and ngModelController.
- The validation state and form state are updated out of the box and you can access them in the view through CSS classes or in the controller using ngFormController properties.
- Although it may not be a ngFormController feature, updateOn and debounce properties of ng-model-options and ngModel parsers and formatters let you control how your form components behave after user interaction.
The good news is, for those using redux, someone has already focused their time on making form development easier and created the amazing tool redux-form. This post presents an insider look at how to develop forms in React Native just like in the good old days when ngFormController would take care of all the hard work.
For those who are unfamiliar with Redux check out the following articles to get started:
Here’s a brief intro to redux-form, from the official docs:
Redux-form primarily consists of four things:
1. A Redux reducer that listens to dispatched redux-form actions to maintain your form state in Redux.
2. A React component decorator that wraps your entire form in a Higher Order Component (HOC) and provides functionality via props.
3. A Field component to connect your individual field inputs to the Redux store.
4. Various Action Creators for interacting with your forms throughout the application.
Summing up, the library includes a HOC to wrap our form and a Field component to wrap our form elements. These two together can provide a similar form workflow as ngFormController and ngModelController leaving you stress-free!
Here we will discover how redux-form will help you with the following form-related features:
- Form and form components state
- Sync validations
- Async validations
- Control user interaction
We will need to create a wrapper around TextInput to use with the redux-form Field component. Our wrapper will execute callbacks and use values provided by redux form to properly track and update the form element state.
The callbacks we will use are provided in the input prop to our wrapper: onBlur, onChange, and onFocus. These allow a proper track of the elements states, which is also provided in the meta prop. With that in mind, our current code should look similar to:
Last but not least, this wrapper component will be used inside a form. That form should use the Field component and the higher order component from redux-form. Here’s a quick example:
Form and components state
The form state attributes will be available as props of the component handled to reduxForm HOC, while the elements’ state will be provided by the meta prop in our wrappers handled to the Field component. That being said, we can quickly tweak our basic examples to provide some info about the current state:
Simple validations can be added to the form in two different ways: a validate function for the entire form and/or validations per field.
Form level validation can be accomplished by providing a validate function to redux-form HOC. That function will receive the forms values and component props as arguments, and should return an object indicating error messages for each field (a field without an error message is considered valid):
On the other hand, field level validations are available using the validate prop of the Field component
After setting proper validations, we can start adding proper styles and error warnings in our inputs using the meta prop. Some useful values include:
- meta.valid: true if the field value passes validation (has no validation errors)
- meta.active: true if the field currently has focus.
- meta.dirty: true if the field value has changed from its initial value.
- meta.error: the error for this field is if its value is not passing validation.
- meta.visited: true if this field has never had focus.
By default, redux-form will not allow submissions if the form is invalid.
To set up async validations per field, redux-form provides the following options for the HOC:
- asyncValidate: a function that takes form values and the changed field and returns a promise that will resolve if the validation passed and will be rejected with an object with errors for each field otherwise.
- shouldAsyncValidate: a function that returns true if an async validation should continue or be interrupted. Receives an object as an argument that will let you customize this behavior
- asyncBlurFields: Which fields should trigger an async validation when blurred
By default, the asyncValidate will be executed after any field is blurred and before submitting the form. To change this behavior you can use asyncBlurFields to trigger the validate only after specific fields or implement shouldAsyncValidate to completely customize the execution of the asynchronous validation.
The following example triggers the asyncValidate only when the email field is blurred:
Control user interaction
In order to customize the user input, there are 3 value lifecycle hooks that we can use: format, parse, and normalize.
Format allows you to alter the value from the Redux store to be displayed in the field input. Parse will alter the value obtained from the view to save it in the store. And, normalize will let you alter the field value based on the values of the form.
The hooks must be used at field level:
You can use these examples to create your own form components like checkboxes, radio buttons or selects, redux-form is very useful with those as well.
These were some of the basic features that can be implemented with redux-form, however, don’t hesitate to check the official docs to improve your forms handling!
(*) The examples in this article can be found in https://github.com/sbalay/react-native-redux-form-example