React - валидация формы и обработка POST запроса

Внимание

Данный материал является частью цикла статей «Создаем приложение на React с использованием Redux». Не забудьте посмотреть другие статьи по этой теме :-)

  1. React - Настройка проекта и подготовка компонентов
  2. React - навигация и маршрутизация
  3. React - использование Redux в приложении React
  4. React – Lazy Loading и компоненты высшего порядка (HOC)
  5. React - обработка исключений
  6. React - создание динамических форм и модальных компонентов
  7. React - валидация формы и обработка POST запроса
  8. React - обработка запроса PUT для редактирования данных
  9. React - обработка запроса на удаление данных

Обработка события OnChange с помощью функции handleChangeEvent

Сейчас, если мы введем что-то в поля ввода, ничего не произойдет. Это потому, что в нашем файле InputConfiguration.js все свойства value установлены на пустые строки для всех входов (кроме datePicker, он принимает функцию moment(), которая всегда применяет текущую дату). Более важно то, что мы никогда не изменяем свойство value внутри объекта конфигурации, то есть пустые поля ввода.

Мы собираемся исправить это и добавить проверку, реализовав функцию handleChangeEvent.

Давайте изменим файл CreateOwner.js, добавив функцию handleChangeEvent под обработчиком жизненного цикла componentWillMount:

handleChangeEvent = (event, id) => {
    const updatedOwnerForm = { ...this.state.ownerForm };
    updatedOwnerForm[id] = formUtilityActions.executeValidationAndReturnFormElement(event, updatedOwnerForm, id);

    const counter = formUtilityActions.countInvalidElements(updatedOwnerForm);

    this.setState({ ownerForm: updatedOwnerForm, isFormValid: counter === 0 })
}

Эта функция принимает два параметра. Параметр event, который содержит значение из наших полей ввода (не только значение, но это важно для нас), и параметр id, которому мы собираемся передать один из ключей объекта конфигурации (имя, адрес или дату рождения).

Мы глубоко клонируем ownerForm из состояния, а затем обновляем конфигурацию текущего измененного входа, вызывая функцию executeValidationAndReturnFormElement. После проверки мы подсчитываем, сколько полей недействительно, и если таковые имеются, наша полная форма будет отмечена как недействительная. Наконец, мы собираемся обновить состояние.

Создание дополнительных функций для проверки формы React

У нас есть функции executeValidationAndReturnFormElement и countInvalidElements, но они еще не созданы.

Итак, давайте создадим их.

Давайте изменим файл FormUtility.js:

export const executeValidationAndReturnFormElement = (event, updatedOwnerForm, id) => {
    let formElement = { ...updatedOwnerForm[id] };
    formElement.value = id === 'dateOfBirth' ? event : event.target.value;
    formElement.touched = true;

    const validationResponse = checkValidity(formElement.value, formElement.validation);

    formElement.valid = validationResponse.isValid;
    formElement.errorMessage = validationResponse.errorMessage;

    return formElement;
}

Во-первых, мы клонируем точный formElement, который пользователь в настоящее время изменяет. Затем заполните значение formElement с помощью тернарного оператора (если пользователь изменяет ввод dateOfBirth, то значением является event. Но для любого другого поля ввода event является объектом, имеющим свойство target, которое содержит свойство value). Кроме того, мы собираемся установить для нашего элемента управления значение касания, а затем отправить значение и правила проверки в процесс проверки. После завершения процесса мы сохраняем эти результаты. Наконец, мы возвращаем formElement со всеми изменениями.

Реализация функции CheckValidity

Итак, давайте реализуем функцию checkValidity непосредственно над функцией executeValidationAndReturnFormElement:

const checkValidity = (value, validation) => {
    let validationObject = {
        isValid: true,
        errorMessage: ''
    };

    if (validation) {
        if (validation.required) {
            validationObject.isValid = value.trim() !== '';
            validationObject.errorMessage = validationObject.isValid ? '' : 'Field is required';
        }

        if (validationObject.isValid && validation.maxLength) {
            validationObject.isValid = value.length <= 60;
            validationObject.errorMessage = 'Not allowed more than 60 charactes';
        }

        return validationObject;
    }
    else {
        return validationObject;
    }
}

С помощью приведенного выше кода мы проверяем, нужно ли нам вообще проверять ввод. Если его нужно проверить, мы выполняем проверку, но если нет (например, dateOfBirth), мы просто возвращаем тот же объект обратно.

В процессе проверки мы проверяем, имеет ли поле необходимое правило проверки, и проверяем его. Затем, если он все еще действителен, мы проверяем его на соответствие правилу maxLength. Наконец, мы просто возвращаем validationObject.

Проверка всей формы React

Внутри функции handleChangeEvent мы вызываем одну дополнительную функцию countInvalidElements. Итак, давайте реализуем эту функцию чуть ниже функции executeValidationAndReturnFormElement в файле FormUtility.js:

export const countInvalidElements = (ownerForm) => {
    let countInvalidElements = 0;
    for (let element in ownerForm) {
        if (!ownerForm[element].valid) {
            countInvalidElements = countInvalidElements + 1;
            break;
        }
    }
    return countInvalidElements;
}

Эта функция довольно проста. Мы перебираем все элементы внутри ownerForm. И если мы обнаруживаем какой-либо недопустимый элемент, мы просто увеличиваем переменную countInvalidElements и выходим из цикла. Наконец, мы возвращаем эту переменную.

Теперь мы можем проверить результаты.

Пустая форма (которую никогда не трогали):

Если мы поместим курсор в поля ввода и оставим:

Другой тип сообщения об ошибке:

Наконец, действительная форма:

Соединяем компонент CreateOwner с редьюсером

Для начала, давайте добавим весь необходимый импорт:

import { connect } from 'react-redux';
import * as repositoryActions from '../../../store/actions/repositoryActions';
import * as errorHandlerActions from '../../../store/actions/errorHandlerActions';

Затем давайте создадим функцию mapStateToProps под компонентом:

const mapStateToProps = (state) => {
    return {
        showSuccessModal: state.repository.showSuccessModal,
        showErrorModal: state.errorHandler.showErrorModal,
        errorMessage: state.errorHandler.errorMessage
    }
}

После этого нам нужно создать функцию mapDispatchToProps ниже mapStateToProps:

const mapDispatchToProps = (dispatch) => {
    return {
        onCreateOwner: (url, owner, props) => dispatch(repositoryActions.postData(url, owner, props)),
        onCloseSuccessModal: (url, props) => dispatch(repositoryActions.closeSuccessModal(props, url)),
        onCloseErrorModal: () => dispatch(errorHandlerActions.closeErrorModal())
    }
}

Затем изменим инструкцию export:

export default connect(mapStateToProps, mapDispatchToProps)(CreateOwner);

Затем, мы собираемся добавить наши модальные компоненты под тегом FormGroup, но все еще внутри тега Well:

<SuccessModal show={this.props.showSuccessModal} 
       modalHeaderText={'Success message'} 
       modalBodyText={'Action completed successfully'}
       okButtonText={'OK'} 
       successClick={() => this.props.onCloseSuccessModal('/owner-List', { ...this.props })} />

<ErrorModal show={this.props.showErrorModal} 
        modalHeaderText={'Error message'} 
        modalBodyText={this.props.errorMessage}
        okButtonText={'OK'} closeModal={() => this.props.onCloseErrorModal()} />

И добавим операторы импорта:

import SuccessModal from '../../../components/Modals/SuccessModal/SuccessModal';
import ErrorModal from '../../../components/Modals/ErrorModal/ErrorModal';

SuccessModal вызывает свойство onCloseSuccessModal из функции mapDispatchToProps. Более того, ErrorModal вызывает свойство onCloseErrorModal из функции mapDispatchToProps. Оба эти свойства являются функциями диспетчеризации, которые не реализованы. Так что это то, что нам нужно исправить.

Изменение действия и файлов Reducer

Нам нужно изменить наши файлы, добавив дополнительные функции для закрытия модальных компонентов успешной отправки и ошибки.

Давайте добавим еще два типа действий в файл ActionTypes.js:

export const CLOSE_ERROR_MODAL = 'CLOSE_ERROR_MODAL';
export const CLOSE_SUCCESS_MODAL = 'CLOSE_SUCCESS_MODAL';

Затем давайте изменим файл repositoryActions.js, добавив функцию closeSuccessModal:

export const closeSuccessModal = (props, url) =>{
    return {
        type: actionTypes.CLOSE_SUCCESS_MODAL,
        props: props,
        url: url
    }
}

Нам также нужно изменить файл repositoryReducer.js:

case actionTypes.CLOSE_SUCCESS_MODAL:
            return executeCloseSuccessModal(state, action)

const executeCloseSuccessModal = (state, action) => {
    action.props.history.push(action.url);
    return {
        ...state,
        showSuccessModal: false
    }
}

Мы закончили с модальным окном успешной отправки, давайте сделаем то же самое для модального окна ошибки.

Во-первых, давайте изменим файл errorHandlerActions.js, добавив новую функцию:

export const closeErrorModal = () => {
    return {
        type: actionTypes.CLOSE_ERROR_MODAL
    }
}

Затем изменим файл errorHandlerReducer.js:

case actionTypes.CLOSE_ERROR_MODAL:
            return executeCloseErrorModal(state, action);

const executeCloseErrorModal = (state, action) => {
    return{
        ...state,
        showErrorModal: false,
        errorMessage: ''
    }
}

Теперь все, что нам нужно сделать, это отправить запрос POST из компонента CreateOwner.

Обработка запроса POST

Внутри тега Form есть событие onSubmit, которое указывает на функцию createOwner. Давайте реализуем эту функцию ниже функции handleChangeEvent:

createOwner = (event) => {
    event.preventDefault();

    const ownerToCreate = {
        name: this.state.ownerForm.name.value,
        address: this.state.ownerForm.address.value,
        dateOfBirth: this.state.ownerForm.dateOfBirth.value
    }

    const url = '/api/owner';
    this.props.onCreateOwner(url, ownerToCreate, { ...this.props });
}

Ниже этой функции нам нужно добавить еще одну функцию для функциональности кнопки Отмена:

redirectToOwnerList = () => {
    this.props.history.push('/owner-List');
}

Теперь мы можем проверить результаты:

В случае возникновения ошибки с кодом состояния, отличным от 404 или 500:

Заключение

Прочитав этот пост, вы узнали:

  • Как обработать событие onChange в поле ввода
  • Как проверить нашу динамически созданную форму
  • Как использовать ранее созданные компоненты модального окна
  • Способ обработки POST-запроса

Спасибо, что прочитали статью, и я надеюсь, что вы нашли в ней что-то полезное.

В следующей части этой серии мы собираемся узнать, как обрабатывать PUT-запросы. Кроме того, мы собираемся использовать различные хуки жизненного цикла в React и узнать, как обновить наше состояние с помощью измененных данных с сервера.