Xử lý form với HTMLFormElement.elements
Hình chụp bởi Kelly Sikkema. Nguồn: Unsplash
Đại loại khi làm form “đơn giản” trong React thì chúng ta hay viết như thế này.
const PLANS = [
{ value: '30-days', label: '30 days' },
{ value: '90-days', label: '90 days' },
{ value: 'lifetime', label: 'Lifetime' },
]
function FormRegister() {
const [email, setEmail] = useState('')
const [plan, setPlan] = useState(PLANS[0].value)
const doSubmit = (e) => {
e.preventDefault()
const payload = { plan, email }
console.log(payload)
}
return (
<form onSubmit={doSubmit}>
<label htmlFor="email">
Email*
<input
type="email"
name="email"
placeholder="hi@local.dev"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</label>
<fieldset required>
<legend>Membership plan*</legend>
{PLANS.map((item) => (
<label htmlFor={item.value} key={item.label}>
<input
required
type="radio"
name="plan"
value={item.value}
onChange={(e) => setPlan(e.target.value)}
checked={item.value === plan}
/>
{item.label}
</label>
))}
</fieldset>
<button type="submit">Register</button>
</form>
)
}
Ở trên sử dụng useState
và controlled components để lưu lại các giá trị do người dùng nhập vào. Ở form này bạn không thực hiện các thao tác phức tạp hơn như validation, hay giá trị của input này phụ thuộc vào một input khác, v.v… Khi người dùng gửi (submit) form, chúng ta sẽ dùng hai states email
và plan
để tạo payload.
Sử dụng HTMLFormElement.elements
Một cách khác là chúng ta có thể dùng HTMLFormElement.elements
trong hàm doSubmit
.
Thuộc tính HTMLFormElement.elements trả về một tập hợp các điều khiển (controls) trong form. Những điều khiển này bao gồm các thẻ <button>
, <fieldset>
, <input>
, <object>
, <output>
, <select>
, và <textarea>
. Một ngoại lệ là thẻ <input type="image">
, vì lý do lịch sử (MDN nói vậy, còn cụ thể ra sao thì mình cũng không biết 🤷♂️).
Bạn có thể truy xuất đến một điều khiển thông qua name
hay id
của nó. Chẳng hạn với form ở trên, chúng ta sẽ làm như sau:
const doSubmit = (e) => {
e.preventDefault()
// `e.target` lúc này chính là `HTMLFormElement`
const plan = e.target.elements.plan.value
const email = e.target.elements.email.value
const payload = { plan, email }
console.log(payload)
}
Các controls cũng có thể được truy xuất thông qua thứ tự nó xuất hiện trong form. Trích dẫn từ MDN.
The form controls in the returned collection are in the same order in which they appear in the form by following a preorder, depth-first traversal of the tree. This is called tree order.
Khi thay đổi vị trí của một control thì thứ tự cũng có thể thay đổi. Do đó sử dụng name
hay id
vẫn là chắc ăn nhất.
Hoặc FormData
Nếu bạn muốn “một phát ăn luôn”, gom hết tất cả giá trị trong form thì sao? Chúng ta có thể dùng FormData
.
const doSubmit = (e) => {
e.preventDefault()
const data = new FormData(e.target)
const input = Object.fromEntries(data.entries())
console.log(input)
}
Kết
FormRegister
sau khi sửa lại trông ngắn gọn và đẹp hơn hẳn.
function FormRegister() {
const doSubmit = (e) => {
e.preventDefault()
const input = Object.fromEntries(new FormData(e.target).entries())
console.log(input)
}
return (
<form onSubmit={doSubmit}>
<label htmlFor="email">
Email*
<input type="email" name="email" placeholder="hi@local.dev" required />
</label>
<fieldset required>
<legend>Membership plan*</legend>
{PLANS.map((item) => (
<label htmlFor={item.value} key={item.label}>
<input required type="radio" name="plan" />
{item.label}
</label>
))}
</fieldset>
<button type="submit">Register</button>
</form>
)
}
🚨 LƯU Ý: Dùng form.elements
hay FormData
rất tiện nếu bạn chỉ muốn thu thập dữ liệu và chuyển qua nơi khác xử lý. Nếu muốn làm những thao tác phức tạp hơn, có lẽ bạn nên dùng react-hook-form
hoặc formik
.