ReactJS: Cách xây dựng biểu mẫu (form) trong React
Giới thiệu
Biểu mẫu là một thành phần quan trọng của các ứng dụng web React. Chúng cho phép người dùng trực tiếp nhập và gửi dữ liệu trong các thành phần khác nhau, từ màn hình đăng nhập đến trang thanh toán. Vì hầu hết các ứng dụng React là các ứng dụng trang đơn (SPA) hoặc các ứng dụng web tải một trang duy nhất qua đó dữ liệu mới được hiển thị động, bạn sẽ không gửi thông tin trực tiếp từ biểu mẫu đến máy chủ. Thay vào đó, bạn sẽ nắm bắt thông tin biểu mẫu ở phía máy khách và gửi hoặc hiển thị nó bằng cách sử dụng mã JavaScript bổ sung.
Các biểu mẫu React đưa ra một thách thức duy nhất vì bạn có thể cho phép trình duyệt xử lý hầu hết các phần tử biểu mẫu và thu thập dữ liệu thông qua các sự kiện thay đổi trong React hoặc bạn có thể sử dụng React để kiểm soát hoàn toàn phần tử bằng cách đặt và cập nhật trực tiếp giá trị đầu vào. Cách tiếp cận đầu tiên được gọi là thành phần không được kiểm soát vì React không thiết lập giá trị. Cách tiếp cận thứ hai được gọi là thành phần được kiểm soát vì React sẽ tích cực cập nhật đầu vào.
Trong hướng dẫn này, bạn sẽ xây dựng biểu mẫu bằng React và xử lý việc gửi biểu mẫu bằng một ứng dụng mẫu gửi yêu cầu mua táo. Bạn cũng sẽ tìm hiểu những ưu điểm và nhược điểm của các component được kiểm soát và không được kiểm soát. Cuối cùng, bạn sẽ tự động đặt các thuộc tính biểu mẫu để bật và tắt các trường tùy thuộc vào trạng thái biểu mẫu. Đến cuối hướng dẫn này, bạn sẽ có thể tạo nhiều biểu mẫu khác nhau bằng cách sử dụng input, checkbox, radiobox, danh sách chọn và hơn thế nữa.
Bước 1 - Tạo một dự án trống
Trong bước này, bạn sẽ tạo một dự án mới bằng Create React App. Sau đó, bạn sẽ xóa dự án mẫu và các tệp liên quan được cài đặt khi bạn khởi động dự án. Cuối cùng, bạn sẽ tạo một cấu trúc tệp đơn giản để tổ chức các component của mình. Điều này sẽ cung cấp cho bạn một cơ sở vững chắc để xây dựng ứng dụng mẫu của hướng dẫn này để tạo style trong bước tiếp theo.
Để bắt đầu, hãy thực hiện một dự án mới. Bạn mở terminal và chạy tập lệnh sau để cài đặt một dự án mới bằng cách sử dụng create-react-app
:
npx create-react-app form-tutorial
Sau khi dự án kết thúc, hãy thay đổi vào thư mục:
cd form-tutorial
Khởi động dự án với lệnh:
npm start
http://localhost:3000/
. Nếu bạn đang chạy điều này từ một máy chủ từ xa, địa chỉ sẽ là .http://your_domain:3000
Trình duyệt của bạn sẽ tải với một ứng dụng React đơn giản được bao gồm như một phần của Create React App:
Bạn sẽ xây dựng một tập hợp các component tùy chỉnh hoàn toàn mới, vì vậy bạn sẽ cần bắt đầu bằng cách xóa một số mã soạn sẵn để bạn có thể có một dự án trống.
Để bắt đầu, hãy mở component src/App.js
. Đây là component gốc được đưa vào trang. Tất cả các component sẽ bắt đầu từ đây. Bạn sửa lại file để trong đó chỉ còn chứa như sau:
import './App.css';
function App() {
return <></>;
}
export default App;
Mở một terminal khác và thực hiện thao tác xóa file logo.svg:
rm src/logo.svg
Tạo thư mục components:
mkdir src/components
Tạo thư mục App:
mkdir src/components/App
Di chuyển các file App.* vào thư mục App:
mv src/App.* src/components/App
Mở file index.js và chỉnh sửa:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
Lưu file lại và quay lại trang web ta được một trang web trống.
Bây giờ bạn đã hoàn thành dự án Tạo ứng dụng React mẫu, hãy tạo một cấu trúc tệp đơn giản. Điều này sẽ giúp bạn giữ cho các component của bạn cô lập và độc lập.
Tạo một thư mục được gọi components
trong thư mục src
, components sẽ là nơi chứa tất cả các component tùy chỉnh của bạn.
Bước 2 - Tạo biểu mẫu cơ bản với JSX
Trong bước này, bạn sẽ tạo một biểu mẫu trống với một phần tử duy nhất và một nút gửi bằng JSX. Bạn sẽ xử lý sự kiện gửi biểu mẫu và truyền dữ liệu sang một dịch vụ khác. Đến cuối bước này, bạn sẽ có một biểu mẫu cơ bản sẽ gửi dữ liệu đến một hàm không đồng bộ.
Để bắt đầu, hãy tạo và mở App.js
ra:
Bạn sẽ xây dựng một biểu mẫu để mua táo. Tạo một <div> với className
là <wrapper>
. Sau đó, thêm một thẻ <h1>
có nội dung "How About Them Apples" và một phần tử form
trống bằng cách thêm mã được đánh dấu như sau:
import './App.css';
function App() {
return (
<div className="wrapper">
<h1>How About Them Apples</h1>
<form>
</form>
</div>
)
}
export default App;
Tiếp theo, bên trong thẻ <form>
, hãy thêm một phần tử <fieldset>
và một phần tử <input>
được bao ngoài bởi một thẻ <label>
. Bằng cách bao ngoài phần tử <input>
bằng một thẻ <label>
, bạn đang hỗ trợ trình đọc màn hình bằng cách liên kết nhãn với input. Điều này sẽ tăng khả năng tiếp cận ứng dụng của bạn.
Cuối cùng, thêm một <button>
submit ở cuối biểu mẫu:
import './App.css';
function App() {
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
<form>
<fieldset>
<label>
<p>Name</p>
<input name="name" />
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Lưu file lại.
Mở App.css
để thiết lập style. Thêm padding
vào .wrapper
và margin
vào fieldset
để cung cấp một số khoảng cách giữa các phần tử:
.wrapper {
padding: 5px 20px;
}
.wrapper fieldset {
margin: 20px 0;
}
Lưu file lại và quay lại trang web, bạn refresh lại trang để được kết quả:
Nếu bạn nhấp vào nút Submit, trang sẽ tải lại. Vì bạn đang xây dựng một ứng dụng trang đơn, bạn sẽ ngăn chặn hành vi tiêu chuẩn này đối với một nút có type="submit"
. Thay vào đó, bạn sẽ xử lý sự kiện submit
bên trong component.
Mở App.js
ra. Để xử lý sự kiện, bạn sẽ thêm một trình xử lý sự kiện vào phần tử <form>
mà không phải <button>
. Tạo một hàm được gọi là handleSubmit
để lấy SyntheticEvent làm đối số. SyntheticEvent
là một trình bao bọc xung quanh đối tượng Event
chuẩn và chứa cùng một giao diện. Gọi .preventDefault
để ngăn trang gửi biểu mẫu, sau đó kích hoạt alert
để cho biết rằng biểu mẫu đã được gửi:
import './App.css';
function App() {
const handleSubmit = event => {
event.preventDefault();
alert('You have submitted the form.')
}
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" />
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Lưu file lại và quay lại trang web, nếu bạn nhấp vào nút gửi, cảnh báo sẽ bật lên, nhưng cửa sổ sẽ không tải lại.
Trong nhiều ứng dụng React, bạn sẽ gửi dữ liệu đến một dịch vụ bên ngoài, chẳng hạn như API Web. Khi dịch vụ xử lý xong, bạn sẽ thường hiển thị thông báo thành công, chuyển hướng người dùng hoặc thực hiện cả hai.
Để mô phỏng một API, hãy thêm một hàm setTimeout
trong hàm handleSubmit
. Thao tác này sẽ tạo ra một hoạt động không đồng bộ đợi một khoảng thời gian nhất định trước khi hoàn thành, hoạt động này hoạt động tương tự như một yêu cầu dữ liệu bên ngoài. Sau đó, sử dụng Hook useState
để tạo một biến submitting
và một hàm setSubmitting
. Gọi setSubmitting(true)
khi dữ liệu được gửi và gọi setSubmitting(false)
khi thời gian chờ được giải quyết:
import { useState } from 'react';
import './App.css';
function App() {
const [submitting, setSubmitting] = useState(false);
const handleSubmit = event => {
event.preventDefault();
setSubmitting(true);
setTimeout(() => {
setSubmitting(false);
}, 3000)
}
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
{submitting &&
<div>Submtting Form...</div>
}
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" />
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Ngoài ra, bạn sẽ thông báo cho người dùng biết form của họ được gửi bằng cách hiển thị một thông báo ngắn trong HTML và sẽ hiển thị khi submitting
là true
.
Lưu file lại và quay lại trang web ta được như sau:
Bây giờ bạn có một biểu mẫu cơ bản xử lý sự kiện gửi bên trong component React. Bạn đã kết nối nó với JSX của mình bằng cách sử dụng trình xử lý sự kiện onSubmit
và bạn sử dụng Hook để hiển thị thông báo có điều kiện trong khi sự kiện handleSubmit
đang chạy.
Trong bước tiếp theo, bạn sẽ thêm nhiều input hơn và lưu dữ liệu vào state khi người dùng điền dữ liệu vào biểu mẫu.
Bước 3 - Thu thập dữ liệu biểu mẫu bằng các component không được kiểm soát
Trong bước này, bạn sẽ thu thập dữ liệu biểu mẫu bằng cách sử dụng các component không được kiểm soát. Component không được kiểm soát là một component không có value
được thiết lập bởi React. Thay vì đặt dữ liệu trên component, bạn sẽ kết nối với sự kiện onChange
để thu thập thông tin đầu vào của người dùng. Khi bạn xây dựng các component, bạn sẽ tìm hiểu cách React xử lý các loại input khác nhau và cách tạo một hàm có thể sử dụng lại để thu thập dữ liệu biểu mẫu vào một đối tượng duy nhất.
Đến cuối bước này, bạn sẽ có thể tạo một biểu mẫu bằng cách sử dụng các phần tử biểu mẫu khác nhau, bao gồm cả danh sách thả xuống và hộp kiểm. Bạn cũng sẽ có thể thu thập, gửi và hiển thị dữ liệu biểu mẫu.
Lưu ý: Trong hầu hết các trường hợp, bạn sẽ sử dụng các component được kiểm soát cho ứng dụng React của mình. Nhưng bạn nên bắt đầu với các component không được kiểm soát để có thể tránh các lỗi nhỏ hoặc các vòng lặp ngẫu nhiên mà bạn có thể đưa vào khi đặt sai giá trị.
Hiện tại, bạn có một biểu mẫu có thể gửi thông tin, nhưng không có gì để gửi. Biểu mẫu có một phần tử <input>
duy nhất , nhưng bạn không thu thập hoặc lưu trữ dữ liệu ở bất kỳ đâu trong component. Để có thể lưu trữ và xử lý dữ liệu khi người dùng gửi biểu mẫu, bạn cần tạo một cách để quản lý trạng thái. Sau đó, bạn sẽ cần kết nối với từng input bằng trình xử lý sự kiện.
Bên trong App.js
, sử dụng Hook useReducer
để tạo một đối tượng formData
và một hàm setFormData
. Đối với hàm reducer, kéo name
và value
từ đối tượng event.target
và cập nhật state
bằng cách mở rộng trạng thái hiện tại trong khi thêm name
và value
vào cuối. Điều này sẽ tạo ra một đối tượng state duy trì trạng thái hiện tại trong khi ghi đè các giá trị cụ thể khi chúng thay đổi:
import { useReducer, useState } from 'react';
import './App.css';
const formReducer = (state, event) => {
return {
...state,
[event.target.name]: event.target.value
}
}
function App() {
const [formData, setFormData] = useReducer(formReducer, {});
const [submitting, setSubmitting] = useState(false);
const handleSubmit = event => {
event.preventDefault();
setSubmitting(true);
setTimeout(() => {
setSubmitting(false);
}, 3000)
}
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
{submitting &&
<div>Submtting Form...</div>
}
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={setFormData}/>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Sau khi tạo reducer, ta đã thêm setFormData
vào trình xử lý sự kiện onChange
trong input. Lưu file lại và quay lại trang web. Tuy nhiên, nếu bạn thử và nhập dữ liệu đầu vào, bạn có thể sẽ gặp lỗi dạng như sau:
Vấn đề là SyntheticEvent
được sử dụng lại và không thể được truyền cho một hàm không đồng bộ. Nói cách khác, bạn không thể truyền sự kiện trực tiếp. Để khắc phục điều này, bạn sẽ cần lấy dữ liệu cần thiết trước khi gọi hàm reducer.
Cập nhật hàm reducer để lấy một đối tượng có thuộc tính name
và value
. Sau đó, tạo một hàm được gọi là handleChange
để kéo dữ liệu từ event.target
và truyền đối tượng đến setFormData
. Cuối cùng, cập nhật trình xử lý sự kiện onChange
để sử dụng hàm mới:
import { useReducer, useState } from 'react';
import './App.css';
const formReducer = (state, event) => {
return {
...state,
[event.name]: event.value
}
}
function App() {
const [formData, setFormData] = useReducer(formReducer, {});
const [submitting, setSubmitting] = useState(false);
const handleSubmit = event => {
event.preventDefault();
setSubmitting(true);
setTimeout(() => {
setSubmitting(false);
}, 3000);
}
const handleChange = event => {
setFormData({
name: event.target.name,
value: event.target.value,
});
}
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
{submitting &&
<div>Submtting Form...</div>
}
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={handleChange}/>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Lưu file lại và quay lại trang web và bạn có thể nhập dữ liệu vào input.
Bây giờ bạn đang thu thập state biểu mẫu, hãy cập nhật thông báo hiển thị người dùng để hiển thị dữ liệu trong phần tử <ul>
.
Chuyển đổi dữ liệu thành một mảng bằng cách sử dụng Object.entries
, sau đó ánh xạ dữ liệu chuyển đổi từng phần tử của mảng thành một phần tử <li>
có tên và giá trị. Hãy chắc chắn sử dụng name
làm prop key
cho phần tử:
...
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
{submitting &&
<div>
You are submitting the following:
<ul>
{Object.entries(formData).map(([name, value]) => (
<li key={name}><strong>{name}</strong>: {value.toString()}</li>
))}
</ul>
</div>
}
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={handleChange}/>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Lưu file lại và quay lại trang web và bạn có thể nhập và gửi dữ liệu:
Bây giờ bạn đã có một biểu mẫu cơ bản, bạn có thể thêm nhiều phần tử hơn. Tạo một phần tử <fieldset>
khác và thêm một phần tử <select>
trong đó có đưa vào các giống táo khác nhau cho mỗi loại <option>
, một <input>
với type="number"
và step="1"
để có số lượng tăng lên 1 và một <input>
có type="checkbox"
cho tùy chọn gói quà.
Đối với mỗi phần tử, hãy thêm hàm handleChange
vào trình xử lý sự kiện onChange
:
...
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
{submitting &&
<div>
You are submitting the following:
<ul>
{Object.entries(formData).map(([name, value]) => (
<li key={name}><strong>{name}</strong>: {value.toString()}</li>
))}
</ul>
</div>
}
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={handleChange}/>
</label>
</fieldset>
<fieldset>
<label>
<p>Apples</p>
<select name="apple" onChange={handleChange}>
<option value="">--Please choose an option--</option>
<option value="fuji">Fuji</option>
<option value="jonathan">Jonathan</option>
<option value="honey-crisp">Honey Crisp</option>
</select>
</label>
<label>
<p>Count</p>
<input type="number" name="count" onChange={handleChange} step="1" />
</label>
<label>
<p>Gift Wrap</p>
<input type="checkbox" name="gift-wrap" onChange={handleChange} />
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Lưu file lại và quay lại trang web bạn sẽ có thể nhập và chọn được nhiều loại dữ liệu khác nhau:
Có một trường hợp đặc biệt ở đây cần xem xét. Hộp kiểm value
dành cho gói quà sẽ luôn là "on"
, bất kể mục đó có được chọn hay không. Thay vì sử dụng sự kiện value
, bạn sẽ cần sử dụng thuộc tính checked
.
Cập nhật hàm handleChange
để xem event.target.type
có checkbox
không. Nếu có thì truyền thuộc tính event.target.checked
làm value
thay vì event.target.value
:
import { useReducer, useState } from 'react';
import './App.css';
...
function App() {
const [formData, setFormData] = useReducer(formReducer, {});
const [submitting, setSubmitting] = useState(false);
const handleSubmit = event => {
event.preventDefault();
setSubmitting(true);
setTimeout(() => {
setSubmitting(false);
}, 3000);
}
const handleChange = event => {
const isCheckbox = event.target.type === 'checkbox';
setFormData({
name: event.target.name,
value: isCheckbox ? event.target.checked : event.target.value,
})
}
...
Trong đoạn code trên bạn đã sử dụng toán tử ba ngôi ?
để tạo câu lệnh điều kiện.
Lưu file lại và quay lại trang web, hãy điền vào biểu mẫu và nhấp vào gửi. Bạn sẽ thấy rằng cảnh báo khớp với dữ liệu trong biểu mẫu:
Như vậy trong bước này, bạn đã học cách tạo các thành phần biểu mẫu không kiểm soát. Bạn đã lưu dữ liệu biểu mẫu vào một state bằng cách sử dụng Hook useReducer
và sử dụng lại dữ liệu đó trong các thành phần khác nhau. Bạn cũng đã thêm các loại thành phần biểu mẫu khác nhau và điều chỉnh hàm để lưu dữ liệu chính xác tùy thuộc vào loại phần tử.
Trong bước tiếp theo, bạn sẽ chuyển đổi các thành phần thành các thành phần được kiểm soát bằng cách đặt động giá trị thành phần.
Bước 4 - Cập nhật dữ liệu biểu mẫu bằng các thành phần được kiểm soát
Trong bước này, bạn sẽ tự động thiết lập và cập nhật dữ liệu bằng các thành phần được kiểm soát. Bạn sẽ thêm một prop value
vào mỗi thành phần để thiết lập hoặc cập nhật dữ liệu biểu mẫu. Bạn cũng sẽ đặt lại dữ liệu biểu mẫu khi gửi.
Đến cuối bước này, bạn sẽ có thể kiểm soát động dữ liệu biểu mẫu bằng cách sử dụng state và prop React.
Với các thành phần không được kiểm soát, bạn không phải lo lắng về việc đồng bộ hóa dữ liệu. Ứng dụng của bạn sẽ luôn tuân theo những thay đổi gần đây nhất. Nhưng có nhiều trường hợp bạn sẽ cần cả đọc từ và ghi vào một thành phần input. Để làm điều này, bạn sẽ cần giá trị của thành phần là động.
Ở bước trước, bạn đã gửi một biểu mẫu. Nhưng sau khi gửi biểu mẫu thành công, biểu mẫu vẫn chứa dữ liệu cũ. Để xóa dữ liệu khỏi mỗi input, bạn sẽ cần thay đổi các thành phần từ các thành phần không được kiểm soát thành các thành phần được kiểm soát.
Một thành phần được kiểm soát tương tự như một thành phần không được kiểm soát, nhưng React cập nhật phần prop value
. Nhược điểm là nếu bạn không cẩn thận và không cập nhật value
đúng cách thì thành phần sẽ bị hỏng và dường như sẽ không cập nhật.
Trong biểu mẫu này, bạn đã lưu trữ dữ liệu, vì vậy để chuyển đổi các thành phần, bạn sẽ cập nhật phần prop value
với dữ liệu từ state formData
. Tuy nhiên, có một vấn đề: value
không thể được undefined
. Nếu giá trị của bạn là undefined
, bạn sẽ nhận được lỗi trong console.
Vì state ban đầu của bạn là một đối tượng trống, bạn sẽ cần đặt giá trị thành giá trị từ formData
hoặc giá trị mặc định, chẳng hạn như một chuỗi trống. Ví dụ: giá trị cho tên sẽ là formData.name || ''
:
...
return(
<div className="wrapper">
<h1>How About Them Apples</h1>
{submitting &&
<div>
You are submitting the following:
<ul>
{Object.entries(formData).map(([name, value]) => (
<li key={name}><strong>{name}</strong>: {value.toString()}</li>
))}
</ul>
</div>
}
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={handleChange} value={formData.name || ''}/>
</label>
</fieldset>
<fieldset>
<label>
<p>Apples</p>
<select name="apple" onChange={handleChange} value={formData.apple || ''}>
<option value="">--Please choose an option--</option>
<option value="fuji">Fuji</option>
<option value="jonathan">Jonathan</option>
<option value="honey-crisp">Honey Crisp</option>
</select>
</label>
<label>
<p>Count</p>
<input type="number" name="count" onChange={handleChange} step="1" value={formData.count || ''}/>
</label>
<label>
<p>Gift Wrap</p>
<input type="checkbox" name="gift-wrap" onChange={handleChange} checked={formData['gift-wrap'] || false}/>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Như ở phần trên, hộp kiểm có một chút khác biệt. Thay vì đặt giá trị, bạn cần đặt thuộc tính checked
. Nếu thuộc tính là true thì trình duyệt sẽ hiển thị hộp như được chọn. Đặt thuộc tính checked
ban đầu thành false với formData['gift-wrap'] || false
.
Nếu bạn muốn điền trước biểu mẫu, hãy thêm một số dữ liệu mặc định vào state formData
. Đặt giá trị mặc định cho giá trị count
bằng cách đặt giá trị formState
mặc định là { count: 100 }
. Bạn cũng có thể đặt các giá trị mặc định trong đối tượng ban đầu, nhưng bạn cần lọc ra các giá trị sai trước khi hiển thị thông tin biểu mẫu:
...
function App() {
const [formData, setFormData] = useReducer(formReducer, {
count: 100
});
const [submitting, setSubmitting] = useState(false);
...
Lưu file lại và quay lại trang web, bạn sẽ thấy input với dữ liệu mặc định:
Bây giờ bạn đã có các thành phần đang hoạt động, bạn có thể xóa dữ liệu khi gửi. Để làm như vậy, hãy thêm một điều kiện mới trong formReducer
. Nếu event.reset
là true, hãy trả về một đối tượng với các giá trị trống cho mỗi phần tử biểu mẫu. Đảm bảo thêm một giá trị cho mỗi input. Nếu bạn trả về một đối tượng trống hoặc một đối tượng không đầy đủ, các thành phần sẽ không cập nhật vì giá trị là undefined
.
Sau khi bạn thêm điều kiện sự kiện mới vào formReducer
, hãy cập nhật hàm submit của bạn để đặt lại trạng thái khi hàm giải quyết:
import { useReducer, useState } from 'react';
import './App.css';
const formReducer = (state, event) => {
if(event.reset) {
return {
apple: '',
count: 0,
name: '',
'gift-wrap': false,
}
}
return {
...state,
[event.name]: event.value
}
}
function App() {
const [formData, setFormData] = useReducer(formReducer, {
count: 100
});
const [submitting, setSubmitting] = useState(false);
const handleSubmit = event => {
event.preventDefault();
setSubmitting(true);
setTimeout(() => {
setSubmitting(false);
setFormData({
reset: true
})
}, 3000);
}
...
Lưu file lại và quay lại trang web, bạn sẽ thấy form sẽ được clear sau khi gửi.
Trong bước này, bạn đã chuyển đổi các thành phần không được kiểm soát của mình thành các thành phần được kiểm soát bằng cách đặt động value
hoặc các thuộc tính checked
. Bạn cũng đã học cách nạp lại dữ liệu bằng cách đặt state mặc định và cách xóa dữ liệu bằng cách cập nhật form reducer để trả về giá trị mặc định.
Trong bước tiếp theo này, bạn sẽ đặt động các thuộc tính thành phần của biểu mẫu và vô hiệu hóa biểu mẫu trong khi nó đang gửi.
Bước 5 - Cập nhật động các thuộc tính biểu mẫu
Trong bước này, bạn sẽ cập nhật động các thuộc tính của phần tử biểu mẫu. Bạn sẽ đặt các thuộc tính dựa trên các lựa chọn trước đó và vô hiệu hóa biểu mẫu của mình trong quá trình gửi để ngăn chặn nhiều lần gửi ngẫu nhiên.
Hiện tại, mỗi thành phần đang là tĩnh. Chúng không thay đổi khi biểu mẫu thay đổi. Trong hầu hết các ứng dụng, biểu mẫu là động. Các trường sẽ thay đổi dựa trên dữ liệu trước đó. Chúng sẽ xác thực và hiển thị lỗi. Chúng có thể biến mất hoặc mở rộng khi bạn điền vào các thành phần khác.
Giống như hầu hết các component React, bạn có thể đặt động các thuộc tính và thuộc tính trên các thành phần và chúng sẽ hiển thị lại khi dữ liệu thay đổi.
Thử đặt một input là disabled
cho đến khi một input khác đáp ứng một điều kiện. Cập nhật hộp kiểm Gift Wrap thành disabled trừ khi người dùng chọn tùy chọn fuji
.
Bên trong App.js
, bạn thêm thuộc tính disabled
vào checkbox. Thuộc tính sẽ thành true nếu formData.apple
là fuji
:
...
<fieldset>
<label>
<p>Apples</p>
<select name="apple" onChange={handleChange} value={formData.apple || ''}>
<option value="">--Please choose an option--</option>
<option value="fuji">Fuji</option>
<option value="jonathan">Jonathan</option>
<option value="honey-crisp">Honey Crisp</option>
</select>
</label>
<label>
<p>Count</p>
<input type="number" name="count" onChange={handleChange} step="1" value={formData.count || ''}/>
</label>
<label>
<p>Gift Wrap</p>
<input
checked={formData['gift-wrap'] || false}
disabled={formData.apple !== 'fuji'}
name="gift-wrap"
onChange={handleChange}
type="checkbox"
/>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App;
Lưu file lại và quay lại trang web, bạn sẽ thấy checkbox sẽ bị disabled theo mặc định:
Nếu bạn chọn loại táo Fuji thì checkbox lại enable (được bật):
Ngoài việc thay đổi thuộc tính trên các thành phần riêng lẻ, bạn có thể sửa đổi toàn bộ nhóm thành phần bằng cách cập nhật thành phần fieldset
.
Ví dụ: bạn có thể vô hiệu hóa form trong khi form đang được gửi. Điều này sẽ ngăn việc submit nhiều lần và ngăn người dùng thay đổi các trường trước khi hàm handleSubmit
xử lý xong hoàn toàn.
Thêm disabled={submitting}
vào từng phần tử <fieldset>
và cả phần tử <button>
nữa:
...
<form onSubmit={handleSubmit}>
<fieldset disabled={submitting}>
<label>
<p>Name</p>
<input name="name" onChange={handleChange} value={formData.name || ''}/>
</label>
</fieldset>
<fieldset disabled={submitting}>
<label>
<p>Apples</p>
<select name="apple" onChange={handleChange} value={formData.apple || ''}>
<option value="">--Please choose an option--</option>
<option value="fuji">Fuji</option>
<option value="jonathan">Jonathan</option>
<option value="honey-crisp">Honey Crisp</option>
</select>
</label>
<label>
<p>Count</p>
<input type="number" name="count" onChange={handleChange} step="1" value={formData.count || ''}/>
</label>
<label>
<p>Gift Wrap</p>
<input
checked={formData['gift-wrap'] || false}
disabled={formData.apple !== 'fuji'}
name="gift-wrap"
onChange={handleChange}
type="checkbox"
/>
</label>
</fieldset>
<button type="submit" disabled={submitting}>Submit</button>
</form>
</div>
)
}
export default App;
Lưu file lại và quay lại trang web, khi bạn gửi biểu mẫu thì các trường sẽ bị vô hiệu hóa cho đến khi hàm submit giải quyết xong hoàn toàn:
Bạn có thể cập nhật bất kỳ thuộc tính nào trên một thành phần input. Điều này rất hữu ích nếu bạn cần thay đổi input maxvalue
chẳng hạn hoặc nếu bạn cần thêm thuộc tính pattern
động để xác thực.
Như vậy trong bước này, bạn đặt động các thuộc tính trên các thành phần biểu mẫu. Bạn đã thêm một thuộc tính để bật hoặc tắt động một thành phần dựa trên đầu vào từ một thành phần khác và bạn đã vô hiệu hóa toàn bộ các phần bằng cách vô hiệu hóa các <fieldset>
.
Phần kết luận
Biểu mẫu là chìa khóa cho các ứng dụng web phong phú. Trong React, bạn có các tùy chọn khác nhau để kết nối và kiểm soát các biểu mẫu và phần tử. Giống như các thành phần khác, bạn có thể cập nhật động các thuộc tính bao gồm các phần tử input với value
. Các thành phần không được kiểm soát là tốt nhất để đơn giản hóa, nhưng có thể không phù hợp với các tình huống khi một thành phần cần được xóa hoặc điền trước dữ liệu. Các thành phần được kiểm soát cung cấp cho bạn nhiều cơ hội hơn để cập nhật dữ liệu, nhưng việc có thể thêm một mức độ trừu tượng khác có thể gây ra lỗi hoặc kết xuất không chủ ý.
Bất kể cách tiếp cận của bạn là gì, React cung cấp cho bạn khả năng tự động cập nhật và điều chỉnh các biểu mẫu của bạn cho phù hợp với nhu cầu của ứng dụng và người dùng của bạn.