Формы
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
Last updated
Was this helpful?