Валидация номера телефона в ReactJS

Йо-йо! Сейчас (июнь 2019) я делаю небольшой сайт для бонусной системы одной известной сети кафе в своём городе. Т.к. все данные я получаю по API я решил сделать SPA на реакте.

Схема взаимодействия на сайте такая: пользователь авторизуется по номеру телефона и из API подтягиваются данные. В API я отправляю данные в формате 89999999999. То есть ни каких +7, скобочек и т.д.

Конечно же встал вопрос как валидировать номер телефона, хотелось сделать всё просто и модно выкачав что-то из npm. Сегодня я расскажу как я решил данную задачу.

Компонент с формой

Код компонента с формой, на примере моей страницы регистрации:

import React, { Component } from 'react';


class Page__REGISTER extends Component{
    constructor(props){
        super(props);
        this.state = {
            phone: '',
            password: '',
            isValidPhone: false,
            isValidPassword: false
        };                      

    }

    
    render(){
        
        return(
            <main>
                <h1>Регистрация</h1>
                <p>Введите ваши данные</p>
                <form method="POST">
                    <label>Ваш телефон</label><br />
                    <input id="name" name="phone" type="tel" /><br />
                    <label>Введите жалаемы пароль</label><br />
                    <input id="pass" name="pass" type="password" /><br />
                    <label>Подтвердите пароль</label><br />
                    <input id="pass_suc" name="pass_suc" type="password" /><br />
                    <button type="submit">Отправить</button></form>
            </main>

        )
    }
}



export default Page__REGISTER;

И так, у нас есть форма, там телефон и два поля для ввода пароля, их мы пока опустим. Сейчас нас интересует только номер телефона. Так же я создал state, в котором будет храниться логин и пароль и данные о валидности.

Обработка onSubmit

На отправку формы я назначу обработчик. В этом поможет синтетическое событие из react’а onSubmit. Для этого в тэге form добавит атрибут onSubmit={this.handlerSubmit}, а в state this.handlerSubmit = this.handlerSubmit.bind(this), ну и конечно же метод handlerSubmit в классе компонента.

Получится вот что:

import React, { Component } from 'react';


class Page__REGISTER extends Component{
    constructor(props){
        super(props);
        this.state = {
            phone: '',
            password: '',
            isValidPhone: false,
            isValidPassword: false
        };                      

        this.handlerSubmit = this.handlerSubmit.bind(this);
    }

    handlerSubmit(event){
        event.preventDefault();
    }
    
    render(){
        
        return(
            <main>
                <h1>Регистрация</h1>
                <p>Введите ваши даннык</p>
                <form method="POST" onSubmit={this.handlerSubmit}>
                    <label>Ваш телефон</label><br />
                    <input id="name" name="phone" type="tel" /><br />
                    <label>Введите жалаемы пароль</label><br />
                    <input id="pass" name="pass" type="password" /><br />
                    <label>Подтвердите пароль</label><br />
                    <input id="pass_suc" name="pass_suc" type="password" /><br />
                    <button type="submit">Отправить</button></form>
            </main>

        )
    }
}



export default Page__REGISTER;

Обратите внимание, что в метод handlerSubmit я передал событие и отменил действия по умолчанию.

Получение номера телефона

Далее я буду описывать код handlerSybmit, чтобы не дублировать много кода.

handlerSubmit(event){
    event.preventDefault();
    const form = event.target;
    const inputsData = form.querySelectorAll('input');
    const arrDataFromForm = {};
    Array.from(inputsData).map((e)=>{
        const name = e.name;
        const value = (name !== 'phone')
        ? e.value // Записываю пароли
        : e.value.replace(/\D/g, '').replace(/^7/, '8'); // Чищу номера телефона от лишних символов
        arrDataFromForm[name] = value;
    });
}

Что я сделал: взял форму из события, получил все input’ы, создал объект в который буду собирать данные, с помощью map() прошёлся по input’ам, в мапе обрабатывал данные и записывал их в свой объект.

Так как мне нужно было отправлять номер телефона на сайт в определённом формате, я сразу отчистил его от +7 и других ненужных символов вот так:

e.value.replace(/\D/g, '').replace(/^7/, '8');

Первый replace удали все не числовые символы, а второй заменил первую 7 на 8. Такой подход позволяет пользователю вводить любой номер и не использовать тупые маски. Для конверсии и удобства это лучшее решение.

Собственно валидация

Наконец мы дошли до того ради чего я писал статью. Для того, чтобы проверить валиден номер телефона или нет мы скачаем либу из npm. Библиотека называется validator, вот её страничка на npm.

Устанавливаем библиотеку:

npm i validator --save

Импортируем в наш компонент:

 import validator from 'validator'; 

Когда мы прошли предыдущие шаги пора изменить наш компонент. Для валидации нам понадобиться метод validator.isMobilePhone(numbel, arr(local)). В этот метод мы передаём номер телефона и локаль относительно которой мы проверяем наш номер телефона. Например в России номера начинаются с +7 или 8 и если в методе мы передадим локаль ru-RU то валидатор проверит начинается номер именно с этих чисел или нет, а так же других правил записи русских номеров телефона.

После преобразований у меня получился следующий компонент:

import React, { Component } from 'react';


class Page__REGISTER extends Component{
    constructor(props){
        super(props);
        this.state = {
            phone: '',
            password: '',
            isValidPhone: false,
            isValidPassword: false
        };                      

        this.handlerSubmit = this.handlerSubmit.bind(this);
        // Создаю ref, чтобы было удобно работать с input'ом 
        this.inputNameRef = React.createRef();
    }

    handlerSubmit(event){
        event.preventDefault();
        const form = event.target;
        const inputsData = form.querySelectorAll('input');
        const arrDataFromForm = {};
        Array.from(inputsData).map((e)=>{
            const name = e.name;
            const value = (name !== 'phone')
            ? e.value // Записываю пароли
            : e.value.replace(/\D/g, '').replace(/^7/, '8'); // Чищу номера телефона от лишних символов
            arrDataFromForm[name] = value;
        });

        // Проверка номера на валидность
        if(validator.isMobilePhone(arrDataFromForm["phone"], ['ru-RU'])){
            // Изменил state
            this.setState({
                isValidPhone:true,
                phone: arrDataFromForm["phone"]
            });
        } else {
            // Предупреждаю, что номер неверный
            alert('Неверный номер телефона');
            // Подсвечиваю input с номером телефона для удобства
            this.inputNameRef.current.focus();  
        }
    }
    
    render(){
        
        return(
            <main>
                <h1>Регистрация</h1>
                <p>Введите ваши даннык</p>
                <form method="POST" onSubmit={this.handlerSubmit}>
                    <label>Ваш телефон</label><br />
                    {// Создаю ref для input'а}
                    <input ref={this.inputNameRef} id="name" name="phone" type="tel" /><br />
                    <label>Введите жалаемы пароль</label><br />
                    <input id="pass" name="pass" type="password" /><br />
                    <label>Подтвердите пароль</label><br />
                    <input id="pass_suc" name="pass_suc" type="password" /><br />
                    <button type="submit">Отправить</button></form>
            </main>

        )
    }
}



export default Page__REGISTER;

Обратите внимание, что я создал ref для удобного обращения к input’у

О validator

В данном модуле есть очень много разных проверок, например является ли строка base64 или нет, является ли сторока email’ом, json’ом и многое другое. А так же в нём есть несколько методов отчистки строк от всякого шлака.