Пожалуйста, опишите ошибку

Нашли баг? Помогите нам его исправить, заполнив эту форму

React js для начинающих: на примере создания форм отправки данных

Сергей Юдинцев
Web-разработчик

Скорее всего, эта статья будет интересна новичкам в React. Сразу надо сказать, что если вы используете React и Redux в качестве инструментов разработки фронтенда, то это облегчит вам жизнь во много раз. Особенно при разработке сложного (или не очень) UI. Исключением не являются также и формы отправки данных.

А начнем, пожалуй, с главного. Во-первых и самых важных — с React вам не нужно производить сбор данных из формы с помощью скрипта, который вытягивает и валидирует значения элементов. И, само-собой, отправка формы будет асинхронной. Это все очень удобно, экономит ваше время и радует пользователя. В React вам достаточно будет создать reducer формы и несколько action-ов, которые будет оповещать reducer об изменении состояния. Ну, и, само собой, нам нужен component, который будет отрисовывать саму форму. Далее подробнее рассмотрим создание каждого из компонентов.

Итак, давайте приступим! Для примера взята самая простая форма, содержащая 2 инпута и кнопку отправки. Но даже самая простая форма нуждается в валидации, простой и понятной. Поэтому я предлагаю дать пользователю возможность нажать на кнопку отправки только когда форма будет корректно заполнена (это не всегда хорошее решение, особенно в формах со сложным форматом вводимых данных, но для нашей как раз то, что нужно).

Вот форма, которую нам с вами нужно сделать. Как видите — ничего сложного.

<

ol>

  • Для начала создадим файл с константами (они понадобятся для указания действия, которое reducer-у нужно выполнить).

    export const SET_DESCRIPTION = 'SET_DESCRIPTION';
    export const SET_TITLE = 'SET_TITLE';
    export const RESET_NEW_EVENT = 'RESET_NEW_EVENT';
    

    Здесь всего 3 константы, по названиям которых понятно, что наш reducer будет уметь делать.

  • Теперь создадим reducer.

    import { SET_DESCRIPTION, SET_TITLE, RESET_NEW_EVENT } from '../constants/NewEvent'
    
    const
       initialState = {
           isValid: false,
           title: '',
           description: ''
       };
    
    export default function newEvent(state = initialState, action) {
       switch (action.type) {
    
           case SET_DESCRIPTION :
               return { ...state,  description: action.payload.description, isValid: action.payload.isValid };
    
           case SET_TITLE :
               return  { ...state, title: action.payload.title, isValid: action.payload.isValid  };
    
           case RESET_NEW_EVENT :
               return  { ...state, title: '', description: '', isValid: false  };
           default :
               return state;
       }
    }
    

    Как видите, мы импортировали константы из предыдущего файла.

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

    const
       initialState = {
           isValid: false,
           title: '',
           description: ''
       };
    

    Обратите внимание, что кроме свойств для хранения значения из полей ввода формы (title, description), наша форма имеет свойство isValid, которое определяет активность кнопки отправки.

    Ну а далее описан сам reducer и все, что он умеет делать.

    Также нужно сказать, что эти данные, которые возвращает наш reducer нам нужно поместить в хранилище Redux. Но об этом вы должны были узнать еще при знакомстве и установке React + Redux.

  • Теперь очередь action-a.

    Именно action определяет, что будет делать reducer. По сути, он просто видит, что в хранилище изменилось и просто отдает это что-то reducer-у.

    import { SET_DESCRIPTION, SET_TITLE, RESET_NEW_EVENT } from '../constants/NewEvent'
    import { UPDATE_EVENT_LIST, SET_ACTIVE_EVENT_KEY } from '../constants/EventList'
    import { SET_NOTIFICATION_DATA } from '../constants/Notification'
    import axios from 'axios' //подключаем обработчик сетевых запросов
    
    //action-creator изменения заголовка
    export function setTitle(title) {
    
       return (dispatch, getState) => {
    
           const
               state = getState(),
               description = state.newEvent.description,
               isValid = !!(title && description); //валидация
    
           dispatch({
               type: SET_TITLE,
               payload: {
                   title: title,
                   isValid: isValid
               }
           })
       }
    }
    
    //action-creator изменения комментария
    export function setDescription(desc) {
    
       return (dispatch, getState) => {
    
           const
               state = getState(),
               title = state.newEvent.title,
               isValid = !!(title && desc); //валидация
    
           dispatch({
               type: SET_DESCRIPTION,
               payload: {
                   description: desc,
                   isValid: isValid
               }
           })
       }
    }
    
    //action-creator отправки формы
    export function publishNewEvent() {
       return (dispatch, getState) => {
           const
               state = getState(),
               newEvent = state.newEvent,
               title = newEvent.title,
               description = newEvent.description,
               data = {
                   title: title,
                   description: description
               };
    
           axios
               .post(`/events/`, data)
               .then((res) => {
                   const
                       savedEvent = res.data; //дальше делаем с ним, что хотим - успех!
    
                   dispatch({
                       type: RESET_NEW_EVENT //обнуляем состояние формы (в хранилище Redux)
                   });
               })
               .catch((err) => {
                  //обрабатываем ошибку как угодно
               })
       }
    }
    

    Ну вот и готов наш action. Ну а обработка ошибок и данных зависит только от потребностей, вашей фантазии и знаний 🙂

  • Теперь нам нужно отрисовать все в компоненте.

    import React, { Component, PropTypes } from 'react'
    
    export default class NewEvent extends Component {
    
       setTitle(e) { //вызов изменения заголовка ивента
           const {
               setTitle
           } = this.props.newEventActions;
    
           const
               title = e.target.value;
    
           setTitle(title);
       }
    
       setDescription(e) { //вызов изменения комментария ивента
    
           const {
               setDescription
           } = this.props.newEventActions;
    
           const
               desc = e.target.value;
    
           setDescription(desc);
       }
    
       publish(e) { //публикация ивента
    
           e.preventDefault();
    
           const {
               publishNewEvent
           } = this.props.newEventActions;
    
           publishNewEvent()
       }
    
       render() {
    
           const {
               title,
               description,
               isValid
           } = this.props;
    
           return (
               <div className={ styles.newEvent }>
                   <div className="partHeader">
                       <span>New Event</span>
                   </div>
                   <form className={ styles.contentBlock }>
                       <input placeholder="Event Name" value={ title } onChange={ ::this.setTitle } />
                       <textarea placeholder="Comment" value={ description } onChange={ ::this.setDescription } />
                       <button className={ styles.publishButton } disabled={ `${ !isValid ? 'disabled' : ''}` } onClick={ ::this.publish }>{ isValid ? 'Publish' : 'Enter correct data' }</button>
                   </form>
               </div>
           )
       }
    }
    
    NewEvent.propTypes = {
       title: PropTypes.string,
       description: PropTypes.string,
       isValid: PropTypes.bool.isRequired,
       newEventActions: PropTypes.object.isRequired,
    };
    

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

  • В завершении хочу сказать, что не нужно держаться за старое, изучая что-то новое, и тогда, вы действительно многому научитесь. Чистого вам кода, и читайте другие статьи в нашем блоге!

    Читать и комментировать