React дозволяє отримати значення <form>
у два способи. Перший полягає в імплементації так званих контрольованих компонентів, а другий — в застосуванні React властивості ref
.
Головною характеристикою контрольованих компонентів є те, що значення, яке відображається прив'язане до стану компоненту. Для того, аби оновити значення, потрібно виконати функцію обробника подій onChange
, прикріпленого до елементу <form>
. Функція оновлює стан компоненту, що у свою чергу оновлює значення елементу форми.
(Перш, ніж ми підемо далі, якщо хочете побачити усі приклади коду до цієї статті: ось вони!)
Ось приклад використання контрольованих компонентів:
import React, { Component } from 'react';
class ControlledCompExample extends Component {
constructor() {
super();
this.state = {
fullName: ''
}
}
handleFullNameChange = (e) => {
this.setState({
fullName: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state.fullName)
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor="fullName">Full Name</label>
<input
type="text"
value={this.state.fullName}
onChange={this.handleFullNameChange}
name="fullName" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default ControlledCompExample;
Значення <input>
у цьому випадку this.state.fullName
(рядки 7 та 26), а функція onChange
— handleFullNameChange
(рядки 10-14 та 27).
Головними перевагами контрольованих форм є:
- Все вже налаштовано для валідації вхідних даних користувача;
- Можна динамічно обробляти інші компоненти, базуючись на значеннях контрольованих форм. Наприклад, значення вибору користувача з меню тварин (собака, кішка і т.п.) може контролювати, які інші компоненти форми (скажімо, чекбокс видів корму) будуть показані.
Недоліком такої системи буде кількість коду. Тут нам потрібна властивість стану, яка буде передана елементу форми як props
, а також потрібна функція, яка буде оновлювати значення цієї властивості.
Це не проблема для однієї форми, втім, якщо у вас велика та складна форма (яка не потребує динамічної обробки чи валідації у реальному часі), вам доведеться писати купу коду, якщо будете занадто багато використовувати контрольовані форми.
Набагато простіше було б використати властивість ref
для того, аби отримати значення <input>
. Різні форми і різні композиції компонентів потребують різних стратегій, тому решта цієї статті розбита на секції.
- Текстові та числові інпути,
<select>
- Передача значень до батьківських елементів
- Радіо перемикачі
- Чекбокси
1. Текстові, числові інпути та селекти
Приведемо приклад використання ref
на текстових та числових інпутах. В атрибуті ref
додамо стрілкову функцію, яка приймає інпут в якості параметру. Я, зазвичай, називаю цей параметр так, як називається і сам елемент (3 рядок):
<input
type="text"
ref={input => this.fullName = input} />
Не дивлячись на це, ви можете найменувати параметр, як забажаєте:
<input
type="number"
ref={cashMoney => this.amount = cashMoney} />
Потім візьмемо параметр і присвоїмо його властивості, що прикріплена до ключового слова this
даного класу. Інпути (тобто, DOM вузол) доступні як this.fullName
та this.amount
. Значення інпутів доступні як this.fullName.value
та this.amount.value
. Така ж сама стратегія працює з елементами <select>
:
<select
ref={select => this.petType = select}
name="petType">
<option value="cat">Cat</option>
<option value="dog">Dog</option>
<option value="ferret">Ferret</option>
</select>
Значення вибраної опції доступне як this.petType.value
.
2. Передача значень до батьківських елементів
Використовуючи контрольовані компоненти, передати значення з дочірнього компонента в батьківський досить просто — значення вже існує в батьку, і передається дочірньому компоненту. Функція onChange
також передається і вона оновлює значення, коли користувач взаємодіє з інтерфейсом.
Побачити, як це працює можна тут.
Втім, ситуація відрізняється, коли ми використовуємо ref
. У цьому разі значення зберігається в DOM вузлі, а тому воно має бути передане вверх до батька.
Для того, аби це зробити ми маємо передати дочірньому компоненту «гак», до якого він у свою чергу прикріплює DOM вузол.
Погляньмо на приклад, перш ніж обговорювати це детальніше:
import React, { Component } from 'react';
class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
console.log('first name:', this.firstName.value);
this.firstName.value = 'Got ya!';
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
label={'Name'}
firstName={input => this.firstName = input} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
function CustomInput(props) {
return (
<div>
<label>{props.label}:</label>
<input type="text" ref={props.firstName}/>
</div>
);
}
export default RefsForm;
Тут ми бачимо компонент форми RefsForm
та компонент інпуту CustomInput
. Зазвичай, стрілкова функція знаходиться на самому інпуті, але тут вона передається в якості властивості (рядки 15 та 27). Оскільки стрілкова функція знаходиться у батьку, this
в this.firstName
також там. Значення дочірнього інпуту присвоєно властивості батьківського компонента this.firstName
, отже батько має доступ до неї.
Батьківский компонент не лише має доступ до DOM вузла інпута, а й може встановлювати значення вузла (стрічка 7 і вище). Як тільки форма буде відправлена, значення інпуту встановлюється як Got ya!
.
Освоєння цього патерну може забрати деякий час, тому майте терпіння.
Замітка: радіо перемикачі та чекбокси все ж краще робити як контрольовані форми, втім, якщо ви націлені використати для них саме ref
, то наступні дві секції для вас.
3. Радіо перемикачі
На відміну від числових та текстових інпутів, радіо перемикачі робляться блоками. Кожен елемент блоку має однакову властивість name
:
<form>
<label>
Cat
<input type="radio" value="cat" name="pet" />
</label>
<label>
Dog
<input type="radio" value="dog" name="pet" />
</label>
<label>
Ferret
<input type="radio" value="ferret" name="pet" />
</label>
<input type="submit" value="Submit" />
</form>
У цьому радіо перемикачі pet
є три опції: cat
, dog
і ferret
.
Оскільки нам потрібен увесь радіо блок, навішувати ref
на кожен <input>
не найкращий варіант. Крім того, на жаль, немає і DOM-вузла, який би відповідав за весь блок радіо.
Отримати значення з радіо перемикача ми можемо у три етапи:
- Прописати
ref
тегу<form>
(стрічка 20 і нижче); - Витягти усі радіо елементи з форми. У даному випадку це
pet
(стрічка 9 і нижче);- Тут повертається список вузлів і значення. У цьому випадку цей список вузлів включає 3 вузли
<input>
і значенняselected
; - Майте на увазі, що список вузлів виглядає, як масив, втім у нього немає методів масиву, що приводить нас до наступного пункту.
- Тут повертається список вузлів і значення. У цьому випадку цей список вузлів включає 3 вузли
- Отримати значення за допомогою крапкової нотації (рядок 13 і нижче).
import React, { Component } from 'react';
class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
// вилучити список вузлів з форми
// це виглядає як масив, але не вистачає методів масиву
const { pet } = this.form;
// набір radio має значення
// перевірка логів для підтвердження
console.log(pet, pet.value);
}
render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<label>
Cat
<input type="radio" value="cat" name="pet" />
</label>
<label>
Dog
<input type="radio" value="dog" name="pet" />
</label>
<label>
Ferret
<input type="radio" value="ferret" name="pet" />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default RefsForm;
Це працює, навіть якщо ви створюєте форму з дочірніх компонентів. Хоча логіки в компонентах написано більше, техніка стягування значень залишається такою ж.
import React, { Component } from 'react';
class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
// вилучити список вузлів з форми
// це виглядає як масив, але не вистачає методів масиву
const { pet } = this.form;
// набір radio має значення
// перевірка логів для підтвердження
console.log(pet, pet.value);
}
render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<RadioSet
setName={'pet'}
setOptions={['cat', 'dog', 'ferret']} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
function RadioSet(props) {
return (
<div>
{props.setOptions.map(option => {
return (
<label
key={option}
style={{textTransform: 'capitalize'}}>
{option}
<input
type="radio"
value={option}
name={props.setName} />
</label>
)
})}
</div>
);
}
export default RefsForm;
4. Чекбокси
На відміну від <input>
типу radio
, чекбокси можуть мати декілька обраних значень, що робить процес отримання цих значень складнішим.
Витягти ці значення можна у п'ять кроків:
- Прописуємо
ref
тегу<form>
(рядок 27 і нижче); - Витягуємо усі чекбокси з форми, у цьому випадку
pet
(рядок 9);- Тут повертається список вузлів і значення. У цьому випадку цей список включає 3 вузли
<input>
і значенняselected
; - Майте на увазі, що список вузлів виглядає, як масив, втім у нього немає методів масиву, що приводить нас до наступного пункту.
- Тут повертається список вузлів і значення. У цьому випадку цей список включає 3 вузли
- Конвертуємо список вузлів у масив, аби ми могли використовувати методи масивів (
checkboxArray
на рядок 12); - Використовуємо
Array.filter()
, аби отримати чекбокси, які були обрані (checkedCheckboxes
на рядок 15); - Використовуємо
Array.map()
, аби зберегти лише значення обраних чекбоксів (checkedCheckboxesValues
на рядок 19).
import React, { Component } from 'react';
class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
// вилучити список вузлів з форми
// це виглядає як масив, але не вистачає методів масиву
const { pet } = this.form;
// конвертація списка вузлів у масив
const checkboxArray = Array.prototype.slice.call(pet);
// вилучити лише відмічені чекбокси
const checkedCheckboxes = checkboxArray.filter(input => input.checked);
console.log('checked array:', checkedCheckboxes);
// використовуємо .map() для вилучення значень з кожного відміченного чекбокса
const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value);
console.log('checked array values:', checkedCheckboxesValues);
}
render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<label>
Cat
<input type="checkbox" value="cat" name="pet" />
</label>
<label>
Dog
<input type="checkbox" value="dog" name="pet" />
</label>
<label>
Ferret
<input type="checkbox" value="ferret" name="pet" />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default RefsForm;
Використання дочірнього компонента чекбоксів працює точно так, як і з радіо перемикачем у попередньому прикладі.
import React, { Component } from 'react';
class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
// вилучити список вузлів з форми
// це виглядає як масив, але не вистачає методів масиву
const { pet } = this.form;
// конвертація списка вузлів у масив
const checkboxArray = Array.prototype.slice.call(pet);
// вилучити лише відмічені чекбокси
const checkedCheckboxes = checkboxArray.filter(input => input.checked);
console.log('checked array:', checkedCheckboxes);
// використовуємо .map() для вилучення значень з кожного відміченного чекбокса
const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value);
console.log('checked array values:', checkedCheckboxesValues);
}
render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<CheckboxSet
setName={'pet'}
setOptions={['cat', 'dog', 'ferret']} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
function CheckboxSet(props) {
return (
<div>
{props.setOptions.map(option => {
return (
<label
key={option}
style={{textTransform: 'capitalize'}}>
{option}
<input
type="checkbox"
value={option}
name={props.setName} />
</label>
)
})}
</div>
);
}
export default RefsForm;
Висновок
Якщо вам не потрібно:
- слідкувати за значенням форми у реальному часі (наприклад, аби обробити наступні компоненти, базуючись на вхідних даних користувача),
- виконувати кастомну валідацію у реальному часі,
тоді варто використати ref
для того, аби отримати дані з елементу форми.
Головною перевагою ref
над контрольованими формами є те, що у більшості випадків нам потрібно писати менше коду. Винятковий випадок з чекбоксами (і в меншій мірі з радіо перемикачем), де кількість коду не набагато менша.
Ще немає коментарів