React - обработка запроса PUT для редактирования данных

Внимание

Данный материал является частью цикла статей «Создаем приложение на 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 - обработка запроса на удаление данных

Подготовка компонента UpdateOwner и настройка маршрутизации

Внутри папки containers мы можем найти папку Owner. Внутри этой папки мы создадим новую и назовем ее UpdateOwner. Давайте создадим внутри новый файл UpdateOwner.js:

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

Добавим базовую логику нашего компонента в файл UpdateOwner.js:

import React, { Component } from 'react';
import { Form, Well, Button, FormGroup, Col } from 'react-bootstrap';
import { returnInputConfiguration } from '../../../Utility/InputConfiguration';

class UpdateOwner extends Component {
    state = {
        ownerForm: {},
        isFormValid: true
    }

    componentWillMount = () => {
        this.setState({ ownerForm: returnInputConfiguration() });
    }

    render() { 
        return (  
            <Well>
                
            </Well>
        )
    }
}
 
export default UpdateOwner;

Итак, мы создали компонент класса с его локальным состоянием. Это состояние имеет те же свойства, что и компонент CreateOwner: ownerForm и isFormValid. Для компонента UpdateOwner нам нужно установить для свойства isFormValid значение true, потому что все поля в форме будут заполнены, как только компонент будет смонтирован. Следовательно, наши поля не будут недействительными при монтировании формы, как это было в компоненте CreateOwner.

В хуке жизненного цикла compnentWillMount мы получаем всю входную конфигурацию и обновляем состояние.

Чтобы включить переход к этому компоненту, нам нужно изменить файл App.js:

import UpdateOwner from './Owner/UpdateOwner/UpdateOwner';
<Route path="/updateOwner/:id" component={UpdateOwner} />

Теперь мы можем перейти к этому компоненту.

Добавление элементов ввода, проверка и двусторонняя привязка

Сразу под функцией render и над блоком return мы собираемся добавить эту строку кода для преобразования объекта ownerForm в массив объектов:

const formElementsArray = formUtilityActions.convertStateToArrayOfFormObjects({ ...this.state.ownerForm });

Не забудьте добавить оператор импорта для компонентов formUtilityActions и Input:

import * as formUtilityActions from '../../../Utility/FormUtility';
import Input from '../../../UI/Inputs/Input';

Затем внутри тега Well мы добавим этот код для отображения элементов ввода и кнопок:

<Form horizontal onSubmit={this.updateOwner}>
    {
	formElementsArray.map(element => {
		return <Input key={element.id} elementType={element.config.element} 
			id={element.id} label={element.config.label}
			type={element.config.type} value={element.config.value} 
			changed={(event) => this.handleChangeEvent(event, element.id)}
			errorMessage={element.config.errorMessage} invalid={!element.config.valid} 
			shouldValidate={element.config.validation}
			touched={element.config.touched} 
			blur={(event) => this.handleChangeEvent(event, element.id)} />
	})
    }
    <br />
    <FormGroup>
	<Col mdOffset={6} md={1}>
		<Button type='submit' bsStyle='info' disabled={!this.state.isFormValid}>Update</Button>
	</Col>
	<Col md={1}>
		<Button bsStyle='danger' onClick={this.redirectToOwnerList}>Cancel</Button>
	</Col>
    </FormGroup>
</Form>

Это код, который мы использовали в одном из предыдущих постов. Давайте добавим функцию 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 })
}

Опять же, это раздел, в котором мы можем повторно использовать код для проверки и двусторонней привязки.

В этот момент, когда мы нажимаем кнопку "Обновить" в компоненте OwnerList, мы увидим представление UpdateOwner:

Это нормально, но мы хотим заполнить все поля правильными данными владельца.

Итак, давайте сделаем именно это.

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

Давайте добавим необходимые операторы импорта в файл UpdateOwner.js:

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

Затем мы собираемся добавить функцию mapStateToProps под компонентом:

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

Чуть ниже функции mapStateToProps нам нужно добавить функцию mapDispatchToProps:

const mapDispatchToProps = (dispatch) => {
    return {
        onGetOwnerById: (url, props) => dispatch(repositoryActions.getData(url, props)),
        onUpdateOwner: (url, owner, props) => dispatch(repositoryActions.putData(url, owner, props)),
        onCloseSuccessModal: (url, props) => dispatch(repositoryActions.closeSuccessModal(props, url)),
        onCloseErrorModal: () => dispatch(errorHandlerActions.closeErrorModal())
    }
}

Наконец, давайте изменим оператор экспорта:

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

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

Сначала добавим операторы импорта:

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

Затем нам нужно добавить компоненты под тегом Form, но внутри тега 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()} />

Получение данных с сервера

Ниже хука жизненного цикла compnentWillMount мы собираемся добавить еще один крючок для получения данных с сервера:

componentDidMount = () => {
    const id = this.props.match.params.id;
    const url = '/api/owner/' + id;
    this.props.onGetOwnerById(url, { ...this.props });
}

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

Перехватчики componentWillMount и componentDidMount являются перехватчиками жизненного цикла создания. Но в React есть перехватчики жизненного цикла update, которые срабатывают, как только новое свойство поступает в компонент или мы обновляем состояние.

Итак, если мы посмотрим на диаграмму, на которой мы объясняли поток Redux, мы можем видеть, что после того, как редуктор обновит состояние, центральное хранилище будет распространять это состояние как props внутри компонента. Мы можем поймать это изменение в этот момент и обновить наш объект ownerForm внутри нашего локального состояния с помощью хука жизненного цикла обновления.

Отображение данных на экране

Поэтому давайте добавим новый хук жизненного цикла обновления под функцией componentDidMount:

componentWillReceiveProps = (nextProps) => {
	const updatedOwnerForm = { ...this.state.ownerForm };
	let nameObject = { ...updatedOwnerForm.name };
	let dateObject = { ...updatedOwnerForm.dateOfBirth };
	let addressObject = { ...updatedOwnerForm.address };

	nameObject.value = nextProps.data.name;
	nameObject.valid = true;
	dateObject.value = moment(nextProps.data.dateOfBirth);
	addressObject.value = nextProps.data.address;
	addressObject.valid = true;

	updatedOwnerForm['name'] = nameObject;
	updatedOwnerForm['dateOfBirth'] = dateObject;
	updatedOwnerForm['address'] = addressObject;
	this.setState({ ownerForm: updatedOwnerForm });
}

Хук componentWillReceiveProps вызывается только тогда, когда компонент получает новый объект props , но не, когда мы обновляем состояние.

Что ж, некоторые из хуков жизненного цикла обновления будут срабатывать, когда прибывают новые реквизиты, а также когда мы обновляем состояние, поэтому, если мы обновим состояние внутри этого хука и не предоставим условие выхода (какой-то тип if оператор), мы получим бесконечный цикл. Но это не относится к хуку componentWillReceiveProps.

Внутри хука componentWillReceiveProps мы используем не оператор this.props, а параметр nextProps, поскольку он содержит наш новый объект props. Таким образом, все действия в этой функции предназначены для неизменного извлечения ownerForm из состояния и помещения его в updatedOwnerform. Затем из updatedOwnerForm мы можем неизменно извлекать все другие объекты (имя, адрес и дату рождения). После этого действия мы можем изменить значения свойств внутри этих объектов и вернуть эти объекты внутри updatedOwnerForm. Наконец, мы можем обновить состояние с помощью нового объекта updatedOwnerForm.

Теперь наша форма при переходе к ней выглядит иначе:

Выполнение действия обновления

В конце давайте добавим две функции для перенаправления на компонент OwnerList, если мы нажмем кнопку "Отмена", и для обновления объекта:

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

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

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

    const url = "/api/owner/" + this.props.data.id;

    this.props.onUpdateOwner(url, ownerToUpdate, {...this.props});
}

Теперь мы можем протестировать функциональность обновления.

Мы можем изменить код на сервере веб-API, чтобы он отвечал успешно, и сообщения об ошибках, чтобы проверить наши модальные компоненты. Кроме того, мы можем протестировать проверку формы, опустив поля ввода или введя более 60 символов в поле ввода адреса.

Заключение

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

  • Как использовать разные хуки жизненного цикла для получения данных с сервера и обновления локального состояния.
  • Как обрабатывать PUT-запросы в нашем проекте

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

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