Работа с формами

В React HTML-элементы формы ведут себя немного иначе по сравнению с DOM-элементами, так как у элементов формы изначально есть внутреннее состояние. К примеру, в эту HTML-форму можно ввести имя:

1.Работа с формами

<form>
  <label>
    Имя:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="Отправить" />
</form>

По умолчанию браузер переходит на другую страницу при отправке HTML-форм, в том числе и этой. Если вас это устраивает, то не надо ничего менять, в React формы работают как обычно. Однако чаще всего форму удобнее обрабатывать с помощью JavaScript-функции, у которой есть доступ к введённым данным. Стандартный способ реализации такого поведения называется «управляемые компоненты».

Управляемые компоненты

В HTML элементы формы, такие как <input>, <textarea> и <select>, обычно сами управляют своим состоянием и обновляют его когда пользователь вводит данные. В React мутабельное состояние обычно содержится в свойстве компонентов state и обновляется только через вызов setState()

Мы можем скомбинировать оба подхода и сделать состояние React-компонента «единственным источником правды». Тогда React-компонент будет рендерить форму и контролировать её поведение в ответ на пользовательский ввод. Значение элемента формы input в этом случае будет контролировать React, а сам элемент будет называться «управляемый компонент».

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

import React, { useState } from 'react';

function NameForm() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    alert('Отправленное имя: ' + value);
    event.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Имя:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <input type="submit" value="Отправить" />
    </form>
  );
}

export default NameForm;

Мы установили атрибут value для поля ввода и теперь в нём всегда будет отображаться значение this.state.value. Состояние React-компонента стало «источником истины». А так как каждое нажатие клавиши вызывает handleChange, который обновляет состояние React-компонента, значение в поле будет обновляться по мере того, как пользователь печатает.

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

Тег textarea

HTML-элемент <textarea> в качестве текста отображает дочерний элемент:

<textarea>
  Привет! Тут просто немного текста внутри тега textarea
</textarea>

В React <textarea> использует атрибут value. Таким образом, форму с <textarea> можно написать почти тем же способом, что и форму с однострочным <input>:

import React, { useState } from 'react';

function EssayForm() {
  const [value, setValue] = useState('Будьте любезны, напишите сочинение о вашем любимом DOM-элементе.');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    alert('Сочинение отправлено: ' + value);
    event.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Сочинение:
        <textarea value={value} onChange={handleChange} />
      </label>
      <input type="submit" value="Отправить" />
    </form>
  );
}

export default EssayForm;

Обратите внимание, что мы инициализировали this.state.value в конструкторе, так что в текстовой области изначально есть текст.

Тег select

В HTML <select> создаёт выпадающий список. HTML-код в этом примере создаёт выпадающий список вкусов:

<select>
  <option value="grapefruit">Грейпфрут</option>
  <option value="lime">Лайм</option>
  <option selected value="coconut">Кокос</option>
  <option value="mango">Манго</option>
</select>

Пункт списка «Кокос» выбран по умолчанию из-за установленного атрибута selected. React вместо этого атрибута использует value в корневом теге select. В управляемом компоненте так удобнее, потому что обновлять значение нужно только в одном месте (state). Пример:

import React, { useState } from 'react';

function FlavorForm() {
  const [value, setValue] = useState('coconut');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    alert('Ваш любимый вкус: ' + value);
    event.preventDefault();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Выберите ваш любимый вкус:
        <select value={value} onChange={handleChange}>
          <option value="grapefruit">Грейпфрут</option>
          <option value="lime">Лайм</option>
          <option value="coconut">Кокос</option>
          <option value="mango">Манго</option>
        </select>
      </label>
      <input type="submit" value="Отправить" />
    </form>
  );
}

export default FlavorForm;

Подводя итог, <input type="text">, <textarea>, и <select> работают очень похоже. Все они принимают атрибут value, который можно использовать, чтобы реализовать управляемый компонент.

Примечание

В атрибут value можно передать массив, что позволит выбрать несколько опций в теге select:

<select multiple={true} value={['Б', 'В']}>

Загрузка файла

В HTML <input type="file"> позволяет пользователю выбрать один или несколько файлов для загрузки с устройства на сервер или управлять им через JavaScript с помощью File API.

<input type="file" />

Так как значение такого элемента доступно только для чтения, это неуправляемый React-компонент.

Обработка нескольких элементов input

Если вам нужны несколько управляемых элементов input, вы можете назначить каждому из них атрибут name, что позволит функции-обработчику решать, что делать, основываясь на значении event.target.name.

Пример:

import React, { useState } from 'react';

function Reservation() {
  const [formState, setFormState] = useState({
    isGoing: true,
    numberOfGuests: 2,
  });

  const handleInputChange = (event) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    setFormState({
      ...formState,
      [name]: value,
    });
  };

  return (
    <form>
      <label>
        Пойдут:
        <input
          name="isGoing"
          type="checkbox"
          checked={formState.isGoing}
          onChange={handleInputChange}
        />
      </label>
      <br />
      <label>
        Количество гостей:
        <input
          name="numberOfGuests"
          type="number"
          value={formState.numberOfGuests}
          onChange={handleInputChange}
        />
      </label>
    </form>
  );
}

export default Reservation;

2. Условный рендеринг

В React не существует специального синтаксиса для описания условий, вместо этого можно использовать обычный код на JavaScript. Например, для условного рендеринга JSX-кода можно применять if:

let content;
if (isLoggedIn) {
  content = <AdminPanel />;
} else {
  content = <LoginForm />;
}
return (
  <div>
    {content}
  </div>
);

Если вы предпочитаете писать более компактный код, используйте условный оператор ?. В отличие от if его можно использовать в JSX:

<div>
  {isLoggedIn ? (
    <AdminPanel />
  ) : (
    <LoginForm />
  )}
</div>

Когда вам не нужна ветка else, можно использовать более короткий логический оператор &&:

<div>
  {isLoggedIn && <AdminPanel />}
</div>

Все эти способы подходят и для задания условий в атрибутах. Если вам не знакомы такие синтаксические конструкции JavaScript, вы можете начать с использования if...else.

3. Рендеринг списков

Для отрисовки списков компонентов вам будет нужно использовать такие возможности JavaScript, как цикл for и функция массива map().

Например, представим, что у вас есть массив продуктов:

const products = [
  { title: 'Капуста', id: 1 },
  { title: 'Чеснок', id: 2 },
  { title: 'Яблоко', id: 3 },
];

Преобразуйте этот массив в массив элементов <li> с помощью функции map() внутри вашего компонента:

const listItems = products.map(product =>
  <li key={product.id}>
    {product.title}
  </li>
);

return (
  <ul>{listItems}</ul>
);

Обратите внимание, что у <li> есть атрибут key. Для каждого элемента списка вам нужно задавать ключ в виде строки или числа, который позволит однозначно отделить этот элемент от остальных в списке. Обычно этот ключ берется из ваших данных, например, это может быть идентификатор из базы данных. React использует эти ключи при добавлении, удалении или изменении порядка элементов.

Last updated