React course
  • Компоненты и коллекции
  • TypeScript
  • Стилизация
  • События и состояния
  • Формы
  • Жизненный цикл
  • Функциональные vs классовые компоненты
  • Основы Redux
  • Redux Toolkit
  • Асинхронный Redux
  • Селекторы
  • React Router
  • Code splitting
  • Паттерны и контекст
  • Анимация
Powered by GitBook
On this page
  • 1. Контролируемый элемент
  • 2. Формы
  • 3. Генерация Id элементов формы
  • 4. Чекбоксы
  • 5. Радиокнопки
  • 6. Селект
  • 7. Дополнительные материалы

Was this helpful?

Формы

1. Контролируемый элемент

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

Паттерн работы с элементами форм очень простой.

  • Поле в state определяет значение атрибута value инпута

  • Событию onChange передается метод изменяющий поле в состоянии

Получаем замкнутый круг.

  • При событии onChange, метод класса обновляет поле в состоянии

  • При изменении состояния происходит ре-рендер

  • Инпут отображается с обновленными данными

class App extends Component {
  state = {
    value: '',
  };

  handleChange = e => {
    this.setState({ value: e.target.value });
  };

  render() {
    const { value } = this.state;

    return <input type="text" value={value} onChange={this.handleChange} />;
  }
}

Получается что не интерфейс определяет какие у нас данные, а наоборот, данные определяют то, что видит пользователь, обновляя DOM при изменении состояния компонента.

2. Формы

Создадим форму регистрации.

class SignUpForm extends Component {
  state = {
    login: '',
  };

  /*
   * Отвечает за обновление состояния
   */
  handleChange = e => {
    this.setState({ login: e.target.value });
  };

  /*
   * Вызывается при отправке формы
   */
  handleSubmit = evt => {
    evt.preventDefault();
    console.log(`Signed up as: ${this.state.login}`);

    // Проп который передается форме для вызова при сабмите
    this.props.onSubmit(this.state.login);
  };

  render() {
    const { login } = this.state;

    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name
          <input
            type="text"
            placeholder="Enter login"
            value={login}
            onChange={this.handleChange}
          />
        </label>

        <button type="submit">Sign up as {login}</button>
      </form>
    );
  }
}

Добавим поля для email и password, а заодно используем очень полезный паттерн для callback-функции передаваемой в onChange.

/*
 * Выносим объект с примитивами в константу чтобы было удобно сбрасывать.
 * Нельзя использовать если в каком-то свойстве состояния хранится сложный тип.
 */
const INITIAL_STATE = {
  login: '',
  email: '',
  password: '',
};

class SignUpForm extends React.Component {
  state = { ...INITIAL_STATE };

  /*
   * Для всех инпутов создаем один обарботчик
   * Различать инпуты будем по атрибуту name
   */
  handleChange = ({ target }) => {
    const { name, value } = target;

    this.setState({ [name]: value });
  };

  handleSubmit = evt => {
    evt.preventDefault();

    const { login, email, password } = this.state;

    console.log(`
      Login: ${login}
      Email: ${email}
      Password: ${password}
    `);

    this.props.onSubmit({ ...this.state });
    this.reset();
  };

  reset = () => {
    this.setState({ ...INITIAL_STATE });
  };

  render() {
    const { login, email, password } = this.state;

    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name
          <input
            type="text"
            placeholder="Enter login"
            name="login"
            value={login}
            onChange={this.handleChange}
          />
        </label>
        <label>
          Email
          <input
            type="email"
            placeholder="Enter email"
            name="email"
            value={email}
            onChange={this.handleChange}
          />
        </label>
        <label>
          Password
          <input
            type="password"
            placeholder="Enter password"
            name="password"
            value={password}
            onChange={this.handleChange}
          />
        </label>

        <button type="submit">Sign up as {login}</button>
      </form>
    );
  }
}

3. Генерация Id элементов формы

Доступность (accessibility, a11y) - очень важная тема в современном вебе. HTML-атрибут for тега label помогает читалкам и другим вспомогательным инструментам. В React он представлен jsx-атрибутом htmlFor.

Для генерации уникальных идентификаторов элементов форм используется следующий подход: для каждого экземляра компонента, при его инициализации, создается набор уникальных идентификаторов, ханящихся на экземпляре. Таким образом, между разными формами получаем уникальные id.

// Можно использовать любой пакет для генерации уникальных строк
import shortid from 'shortid';

class Form extends React.Component {
  loginInputId = shortid.generate();

  render() {
    return (
      <form>
        <label htmlFor={this.loginInputId}>Login</label>
        <input type="text" name="login" id={this.loginInputId} />
      </form>
    );
  }
}

4. Чекбоксы

Работа с чекбоксами проста и понятна. Чекбокс может быть всего в 2-х состояниях: true или false.

Особенности чекбоксов:

  • Имя атрибута которому передается текущее значение из state. Для чекбоксов это checked, и передаем туда буль

  • При обработке события onChange, для получения значения, в объекте события обращаемся к свойству event.target.checked

  • Если чекбокс должен хранить значение, его так же можно повесить на атрибут value и прочитать из объекта события

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

const INITIAL_STATE = {
  login: '',
  email: '',
  password: '',
  agreed: false,
};

class SignUpForm extends React.Component {
  state = {
    ...INITIAL_STATE,
  };

  handleChange = ({ target }) => {
    const { name, value, type, checked } = target;

    // Если тип элемента checkbox, берем значение checked,
    // в противном случае value
    this.setState({ [name]: type === 'checkbox' ? checked : value });
  };

  handleSubmit = e => {
    e.preventDefault();

    const { login, email, password, agreed } = this.state;

    console.log(`
      Login: ${login}
      Email: ${email}
      Password: ${password}
      Agreed: ${agreed}
    `);

    /* ... */
  };

  render() {
    const { login, email, password, agreed } = this.state;

    return (
      <form onSubmit={this.handleSubmit}>
        {/* ... */}
        <label>
          Agree to terms
          <input
            type="checkbox"
            checked={agreed}
            onChange={this.handleChange}
          />
        </label>

        <button type="submit" disabled={!agreed}>
          Sign up as {login}
        </button>
      </form>
    );
  }
}

5. Радиокнопки

В отличии от привычной группировки по значению атрибута name, в React радиокнопка это всего лишь элемент интерфейса, который:

  • Знает выбран ли он

  • Может попросить форму изменить выделение

Обычно у радиокнопок есть и атрибут checked и value. К примеру радио кнопка отвечающая за выбор пола пользователя.

<input
  type="radio"
  checked={this.state.gender === 'male'}
  value="male"
  onChage={this.handleGenderChange}
/>

Добавим группу радиокнопок в нашу форму.

/*
 * Используем Enumerable чтобы не болеть антипаттерном "магические числа"
 */
const Gender = {
  MALE: 'male',
  FEMALE: 'female',
};

const INITIAL_STATE = {
  login: '',
  email: '',
  password: '',
  agreed: false,
  gender: null,
};

class SignUpForm extends React.Component {
  state = {
    ...INITIAL_STATE,
  };

  /*... */

  render() {
    const { login, email, password, agreed, gender } = this.state;

    return (
      <form onSubmit={this.handleSubmit}>
        {/* ... */}

        <section>
          <h2>Choose your gender</h2>
          <label>
            Male
            <input
              type="radio"
              checked={gender === Gender.MALE}
              name="gender"
              value={Gender.MALE}
              onChange={this.handleChange}
            />
          </label>
          <label>
            Female
            <input
              type="radio"
              checked={gender === Gender.FEMALE}
              name="gender"
              value={Gender.FEMALE}
              onChange={this.handleChange}
            />
          </label>
        </section>

        <button type="submit" disabled={!agreed}>
          Sign up as {login}
        </button>
      </form>
    );
  }
}

6. Селект

Все просто - есть селект, есть опции, у селекта есть value и onChange. Добавляем выбор возрастной категории.

const INITIAL_STATE = {
  login: '',
  email: '',
  password: '',
  agreed: false,
  gender: null,
  age: '',
};

class SignUpForm extends React.Component {
  state = {
    ...INITIAL_STATE,
  };

  /* ... */

  render() {
    const { login, email, password, agreed, gender, age } = this.state;

    return (
      <form onSubmit={this.handleSubmit}>
        {/* ... */}

        <label>
          Choose your age
          <select name="age" value={age} onChange={this.handleChange}>
            <option value="" disabled>
              ...
            </option>
            <option value="18-25">18-25</option>
            <option value="26-35">26-35</option>
            <option value="36+">36+</option>
          </select>
        </label>

        <button type="submit" disabled={!agreed}>
          Sign up as {login}
        </button>
      </form>
    );
  }
}

7. Дополнительные материалы

  • Controlled Components

PreviousСобытия и состоянияNextЖизненный цикл

Last updated 4 years ago

Was this helpful?

Документация - формы
React Final Form