React – Material UI – Validate Masked Phone Number Input Field

Table of Contents
Overview
This is a step by step on how to create a component that validates a masked phone field using Material UI and React Number Format library. You can get the code for this post at the Full Stack Soup GitHub Repo here. The input uses the react-text-mask library and Material UI to build a phone input field that only accepts 10 digit phone numbers. The field will have the standard blue borders unless it has been touched. Using the error properties the field will have red borders if the number does not meet the 10 digit validation rule.
Other Related Articles
Please check out the following article with source files on GitHub.
Source Files For This Article
Get Source CodePrerequisite
Create a new React-App.
npx create-react-app my-app
cd my-app
React Material UI Version 4
Install the Material Core with Node Package Manager.
npm install @material-ui/core @material-ui/icons
React Input Mask
Install the React Text Mask with Node Package Manager.
npm install react-input-mask
Parent Form Class Component
Create a new class component labeled FormParent.js. Add the following imports and state variables. Grid is used to align the field and submit button.
import React, {Component} from 'react';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import InputPhoneField from './InputPhoneField';
export default class OfferSupportForm extends React.Component {
constructor(props) {
super(props);
this.state = {
Phone: '',
IsValidPhone: false,
};
this.handlePhoneNumberChange.bind(this);
}
Handle Input and Submit Events
Handle both phone number and submit events.
handlePhoneNumberChange = (value, isValid) => {
this.setState({Phone: value, isValidPhone: isValid}); }
handleSubmitForm = (event) => {
console.log('handleSubmitForm ', event);
}
Render Phone Field
Render the Input Field and the Submit Button. Notice the submit button is disabled until IsValidPhone is equal to true.
render() {
return (
<>
<Grid container spacing={1} style={{marginTop: '20px'}}>
<Grid sm={4} align="left"></Grid>
<Grid sm={1} >
<InputPhoneField helperText="(Required)"
label="Phone"
fieldName="Phone"
handleChange={this.handlePhoneNumberChange} />
</Grid>
<Grid sm={1} align="right" className="fieldLayoutT25B25R5">
{this.state.IsValidPhone === true ?
<Button variant="contained" color="primary" onClick={this.handleSubmitForm}>
Submit
</Button>
:
<Button variant="contained" color="primary" disabled>
Submit
</Button>
}
</Grid>
</Grid>
</>
);
}
Masked Input Component
Create a new JavaScript file labeled InputPhoneFIeld.js and import the following libraries.
import React from 'react';
import PropTypes from 'prop-types';
import MaskedInput from 'react-text-mask';
import InputLabel from '@material-ui/core/InputLabel';
import FormControl from '@material-ui/core/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText';
import OutlinedInput from '@material-ui/core/OutlinedInput';
Masking the Input
Create a function using <MaskedInput /> to define the Mask and valid characters, in this case numbers.
The mask{[‘(‘, /[1-9]/, /\d/, /\d/, ‘)’, ‘ ‘, /\d/, /\d/, /\d/, ‘-‘, /\d/, /\d/, /\d/, /\d/]} will allow three digit area code in braces following by another seven numbers with a hyphen after the third character.
function TextMaskCustom(props) {
const { inputRef, ...other } = props;
return (
<MaskedInput
{...other}
ref={(ref) => {
inputRef(ref ? ref.inputElement : null);
}}
mask={['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]}
placeholderChar={'\u2000'}
showMask
/>
);
}
In the function portion of the component set the phone number state and the change event handler. The handleChange() will count the number of digits 0-9 and if there are 10 digits, set a boolean variable to true. Both the phone n umber and validation boolean variable are sent to the parent by props.handlePhoneNumberChange( val, isValid ).
export default function InputMaskedPhoneField(props) {
const [phoneNumber, setPhoneNumber] = React.useState(0)
const handleChange = event => {
const val = event.target.value;
let count = 0;
for (let i = 0; i < val.length; i++)
if (val.charAt(i) in [0,1,2,3,4,5,6,7,8,9])
count++
var isValid = (count === 10) ? true : false;
setPhoneNumber(val);
props.handlePhoneNumberChange(val, isValid);
}
Render the Phone Number Input Field
Render the TextMaskCustom() function in OutlinedInput
Set the inputComponent property to TextMaskCustom().
The input field will render as an outlined field with the label embedded in the upper left using the InputLabelProps setting shrink to true.
return (
<>
<FormControl variant="outlined" size={'small'} >
<InputLabel htmlFor="phone" >Phone</InputLabel>
<OutlinedInput
onChange={(e) => handleChange(e)}
name="phone"
label="Phone"
size={'small'}
value={phoneNumber}
// Shrink Label
InputLabelProps={{
shrink: true,
}}
inputComponent={TextMaskCustom}
/>
<FormHelperText id="my-helper-text">{props.helperText}</FormHelperText>
</FormControl>
</>
);
Material Field Error Property

In addition to disabling a Submit Button we can use the error property in Material <TextField /> which will show a red border input. Before showing an error when the form hasn’t been touched we can set another variable to track if the field is pristine or dirty. In the example below we just want to know is the field is dirty (has been modified by the user). Also we can contain the states inside the input component.
const [isValid, setIsValid] = useState(false)
const [dirty, setDirty] = useState(false);
Show Error
Using the error property to show a red border if the field does not have a valid phone number.

error={dirty && !isValid}
Pristine or Dirty Input
Using the onBlur() event, set the dirty to true. This means the user touched the field and went elsewhere on the form.
<OutlinedInput
error={dirty && isValid === false}
onBlur={() => setDirty(true)}
onChange={(e) => handleChange(e)}
name="phone"
label="Phone"
size={'small'}
value={phoneNumber}
InputLabelProps={{
shrink: true,
}}
inputComponent={TextMaskCustom}
/>
Validation With Formik
Formik with Yup are the most commonly used form validation library combo for react. The method above is easier to understand at first but can result in more code to maintain.
Other Articles related to Form Validation with Formik & Yup
Please check out the following articles each with source files for a working example on GitHub.
React Form Validation with Formik & Yup Click Here.
React Stepper Form Validation with Formik & Yup Click Here.
Install Formik and Yup
Formik and Yup are used for form validation.
npm i formik
npm i yup
Yup Validation Schema
Import the following libraries
import { useFormik } from 'formik';
import * as yup from 'yup';
Create a validation schema with Yup, This can validate against the masked phone number Regex.
const phoneRegExp = /^((\+[1-9]{1,4}[ -]?)|(\([0-9]{2,3}\)[ -]?)|([0-9]{2,4})[ -]?)*?[0-9]{3,4}[ -]?[0-9]{3,4}$/;
const validationSchema = yup.object({
phone: yup
.string('Enter your phone number')
.matches(phoneRegExp, 'Phone number is not valid')
.required('Phone is required'),
});
Formik
Create a formik function with the useFormik() hook. The validationSchema defined with Yup will check if the input is valid and if it passes then submit the form.
const formik = useFormik({
initialValues: {
phone: '',
},
validationSchema: validationSchema ,
onSubmit: values => {
// Handle Submit
},
});
Masked Input
The input events and properties, onChange, helperText, error, and value are handled by formik.
<OutlinedInput
name="phone"
label="Phone"
size={'small'}
value={props.formik.values.phone}
error={props.formik.touched.phone && Boolean(props.formik.errors.phone)}
helperText={props.formik.touched.phone && props.formik.errors.phone}
onChange={props.formik.handleChange}
InputLabelProps={{
shrink: true,
}}
inputComponent={TextMaskCustom}
/>
Submit Form
The onClick calls the formik.handleSubmit event
<Button variant="contained" color="primary" onClick={formik.handleSubmit}>
Submit
</Button>
You must be logged in to post a comment.