This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.
Form validation creates input boundaries / requirements for data entry. Form validation in Ext JS is handled by normalizing the validation work within the form fields components. With HTML5’s form fields (IE10+), we can let the input
s handle much of the validation work for us. Let’s look at an example of how we might author a custom React field component to display the form fields’ validation messages in the UI.
Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.
Custom Form Field
First, let’s define a custom Field class where the field type and other particular can be passed in or use a predefined default. We’ll also look at incorporating some layout help from the Pure CSS package.
npm install --save purecss
The Field class:
import React, { Component } from 'react'; import '../node_modules/purecss/build/base.css'; import '../node_modules/purecss/build/forms.css'; import './Field.css'; class Field extends Component { static defaultProps = { value: '', type: 'text' } state = { invalidMsg: null, value: this.props.value } render () { const { label, onChange } = this.props; const { value, invalidMsg } = this.state; const inputProps = { ...this.props, value, onChange: (e) => this.handleChange(e, onChange) }; return ( <div className="pure-control-group"> <label>{label} <input {...inputProps} style={{minWidth: '200px'}} /> { invalidMsg ? <span className="pure-form-message-inline">{invalidMsg}</span> : null } </label> </div> ); } handleChange (e, onChange) { this.setState({ value: e.target.value }); this.validateField(e); if (onChange && typeof onChange === 'function') { onChange(e); } } validateField (e) { const { target } = e, { validity } = target, { valueMissing, valid } = validity; let invalidMsg; if (valueMissing) { invalidMsg = 'This is a required field'; } else if (!valid) { invalidMsg = this.props.invalidMsg || target.validationMessage; } this.setState({ invalidMsg }); } } export default Field;
We can then use the Field component in our App view:
class App extends Component { render() { return ( <div> <Field required name="test" label="required text field " onChange={(e) => console.log('change', e)} /><br/> <Field type="number" max={20} name="test" label="number field (20 max) " onChange={(e) => console.log('change', e)} /> </div> ) } } export default App;
Custom Form Field Explained
That’s a good chunk of code. Let’s break down what we’re doing here. At the top we have our imports, including React and the CSS we’ll use to style the form on the page. For this example, we’re using the Pure CSS library to add a little bit of styling / layout support for our form elements. You’ll see the Pure CSS classes used as className
values in our component’s JSX returned from the render
method. In addition, we’ll add a bit of styling in our own {appRoot}/src/Field.css
file that will make our validation message red:
input:invalid + .pure-form-message-inline { color: #c61c06; }
Custom Form Field Class Methods
handleChange
- For each input change we’ll first set the value on our component state giving us our controlled component
- Next, we’ll call our
validateField
method which will add the invalid message to the UI if applicable - Finally, we’ll call any
onChange
handler that may have been passed in as a prop when the field component was instantiated
validateField
- Initially, we collect up a few variables
target
is the<input>
field element we’re validatingvalidity
is the ValidityState object of the input fieldvalueMissing
will be true if our field is a required field and has no valuevalid
is false if the input is invalid for any reason
- If the field is required and missing a value we’ll display our own message, else we’ll display the validation message from the field itself
- Finally, we set the validation message on the component’s state. This will cause the component to re-render and display the validation error if one was discovered.
render
- First, we extract a number of const variables from the
props
and thestate
object to be used as values in our rendered elements - Next, the props passed in along with the id, value from the component’s state, and the
onChange
handler are added to a props object to be passed to theinput
field of our output JSX - Finally, we output our field’s HTML elements using the component’s state / props:
- A wrapping
div
used to create rows of input elements (versus inline) - A label for our input field
- The input element with the type passed in as a prop (defaults to type “text”)
- The span used to display the validation message (only rendered if
invalidMsg
has been passed in or set by our component’svalidateField
method)
- A wrapping
Wrap Up
The above custom validation solution leverages the HTML5 properties only available to select browsers. So, what if you need to support older browsers such as IE9 and before? Instead of reading from the fields’ built in validation messages / state you could add your own validation logic and messaging logic. The Joi package is a popular validation package that could be used in place of HTML5 validations in your React projects. React-form and Formik are two popular form packages which provide their own form field validation, allowing you to define the validation schema and error message for each form field.
Seth Lemmons
Related Posts
-
Ext JS to React: Form Submission
This is part of the Ext JS to React blog series. You can review the…
-
Ext JS to React: Migration to Open Source
Worried about Migrating from Ext JS? Modus has the Answers Ideraโs acquisition of Sencha has…