Пошаговые формы позволяют разбить ввод данных на отдельные этапы. Рассмотрим, как сделать подобную форму. Допустим, пользователь вводит сначала имя, потом email-адрес и в конце пароль. И при этом пользователю последовательно отображаются форма ввода имени, затем форма ввода email и форма ввода пароля.
Для решения этой задачи определим следующую html-страницу:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>METANIT.COM</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel" data-type="module">
import React from "https://esm.sh/react@19?dev";
import ReactDOM from "https://esm.sh/react-dom@19/client?dev";
function UserForm(props) {
const [formData, setFormData] = React.useState(
{
currentStep: 1,
username: "",
email: "",
password: "",
}
);
const handleChange = (e) => {
const {name, value} = e.target;
setFormData({...formData, [name]: value});
}
const handleSubmit = (e) => {
e.preventDefault()
const { email, username, password } = formData;
alert(`Введенные данные: \nИмя: ${username} \nEmail: ${email} \nПароль: ${password}`);
}
const _next = () => {
let currentStep = formData.currentStep;
currentStep = currentStep >= 2? 3: currentStep + 1;
setFormData({...formData, currentStep: currentStep});
}
const _prev = () => {
let currentStep = formData.currentStep
currentStep = currentStep <= 1? 1: currentStep - 1;
setFormData({...formData, currentStep: currentStep});
}
const previousButton = () =>{
const currentStep = formData.currentStep;
if(currentStep !==1){
return (
<button type="button" onClick={_prev}>Назад</button>
)
}
return null;
}
const nextButton = ()=>{
const currentStep = formData.currentStep;
if(currentStep <3){
return (
<button type="button" onClick={_next}>Вперед</button>
)
}
return null;
}
const sendButton = ()=>{
const currentStep = formData.currentStep;
if(currentStep === 3){
return (
<button>Отправить</button>
)
}
return null;
}
return (
<div>
<h1>Форма входа</h1>
<p>Шаг {formData.currentStep} </p>
<form onSubmit={handleSubmit}>
<UserNameStep
currentStep={formData.currentStep}
handleChange={handleChange}
username={formData.username}
/>
<EmailStep
currentStep={formData.currentStep}
handleChange={handleChange}
email={formData.email}
/>
<PasswordStep
currentStep={formData.currentStep}
handleChange={handleChange}
password={formData.password}
/>
<p>
{previousButton()}
{nextButton()}
{sendButton()}
</p>
</form>
</div>
);
}
function UserNameStep(props){
if (props.currentStep !== 1) return null;
return(
<div>
<label>Имя</label>
<input
name="username"
type="text"
placeholder="Введите имя"
value={props.username}
onChange={props.handleChange}
/>
</div>
);
}
function EmailStep(props){
if (props.currentStep !== 2) return null;
return(
<div>
<label>Email</label>
<input
name="email"
type="text"
placeholder="Введите email"
value={props.email}
onChange={props.handleChange}
/>
</div>
);
}
function PasswordStep(props){
if (props.currentStep !== 3) return null;
return(
<div>
<label>Пароль</label>
<input
name="password"
type="password"
placeholder="Введите пароль"
value={props.password}
onChange={props.handleChange}
/>
</div>
);
}
ReactDOM.createRoot(
document.getElementById("app")
)
.render(<UserForm />);
</script>
</body>
</html>
Рассмотрим, что тут опеделено. Прежде всего, отдельные этапы выделены в отдельные компоненты. Например, компонент для ввода имени:
function UserNameStep(props){
if (props.currentStep !== 1) return null;
return(
<div>
<label>Имя</label>
<input
name="username"
type="text"
placeholder="Введите имя"
value={props.username}
onChange={props.handleChange}
/>
</div>
);
}
Каждый компонент-этап получает через props номер текущего этапа (props.currentStep), отображаемое значение (в случае с UserNameStep это props.username) и
функцию обработчика события ввода props.handleChange. Все эти данные будут передаваться из главного компонента.
Главный компонент UserForm будет хранить в state все вводимые данные:
function UserForm(props) {
const [formData, setFormData] = React.useState(
{
currentStep: 1,
username: "",
email: "",
password: "",
}
);
С помощью свойства currentStep будет отслеживаться текущий этап. По умолчанию это первый этап.
Для обработки ввода определен один общий обработчик, поскольку в данному случае ввод для всех трех компонентов выглядит однотипным:
const handleChange = (e) => {
const {name, value} = e.target;
setFormData({...formData, [name]: value});
}
Через name получаем название поля ввода (атрибут name). И тут важно, что оно соответствует
названию свойства из state, для ввода которого оно используется. Поэтому мы сможем использовать выражение [name]: value для
установки свойства в state.
Тем не менее мы могли бы также определить отдельные обработчики для каждого поля, особенно на том случай, когда для каждого поля потребовалась какая-то своя изощренная валидация.
Когда пользователь закончит ввод и нажмет на кнопку условной отправки, будет срабатывать другой обработчик:
const handleSubmit = (e) => {
e.preventDefault()
const { email, username, password } = formData;
alert(`Введенные данные: \nИмя: ${username} \nEmail: ${email} \nПароль: ${password}`);
}
Здесь просто выводим введенные данные в окне сообщения.
Кроме того, для перехода по шагам вперед-назад определены две дополнительные функции:
const _next = () => {
let currentStep = formData.currentStep;
currentStep = currentStep >= 2? 3: currentStep + 1;
setFormData({...formData, currentStep: currentStep});
}
const _prev = () => {
let currentStep = formData.currentStep
currentStep = currentStep <= 1? 1: currentStep - 1;
setFormData({...formData, currentStep: currentStep});
}
В данном случае просто переключаем текущий шаг, учитывая, что у нас всего 3 этапа.
Для управления перехода по шагам определены три дополнительные функции, которые в зависимости от текущего шага возвращают кнопки:
const previousButton = () =>{
const currentStep = formData.currentStep;
if(currentStep !==1){
return (
<button type="button" onClick={_prev}>Назад</button>
)
}
return null;
}
const nextButton = ()=>{
const currentStep = formData.currentStep;
if(currentStep <3){
return (
<button type="button" onClick={_next}>Вперед</button>
)
}
return null;
}
const sendButton = ()=>{
const currentStep = formData.currentStep;
if(currentStep === 3){
return (
<button>Отправить</button>
)
}
return null;
}
И в конце главного компонента все это выводится на html-страницу:
return (
<div>
<h1>Форма входа</h1>
<p>Шаг {formData.currentStep} </p>
<form onSubmit={handleSubmit}>
<UserNameStep
currentStep={formData.currentStep}
handleChange={handleChange}
username={formData.username}
/>
<EmailStep
currentStep={formData.currentStep}
handleChange={handleChange}
email={formData.email}
/>
<PasswordStep
currentStep={formData.currentStep}
handleChange={handleChange}
password={formData.password}
/>
<p>
{previousButton()}
{nextButton()}
{sendButton()}
</p>
</form>
</div>
);
Визуально это будет выглядеть следующим образом. Сначала отображается форма для ввода имени:
Далее по нажатию на кнопку происходит переход к форме ввода email:
И в конце отображается форма ввода пароля с кнопкой отправки, по нажатию на которую отображается окно с введенными данными:
Аналогичный пример на компонентах-классах:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>METANIT.COM</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel" data-type="module">
import React from "https://esm.sh/react@19?dev";
import ReactDOM from "https://esm.sh/react-dom@19/client?dev";
class UserForm extends React.Component {
constructor(props) {
super(props)
this.state = {
currentStep: 1,
username: "",
email: "",
password: "",
}
}
handleChange = event => {
const {name, value} = event.target
this.setState({
[name]: value
})
}
handleSubmit = event => {
event.preventDefault()
const { email, username, password } = this.state;
alert(`Введенные данные: \nИмя: ${username} \nEmail: ${email} \nПароль: ${password}`)
}
_next = () => {
let currentStep = this.state.currentStep
currentStep = currentStep >= 2? 3: currentStep + 1
this.setState({
currentStep: currentStep
})
}
_prev = () => {
let currentStep = this.state.currentStep
currentStep = currentStep <= 1? 1: currentStep - 1
this.setState({
currentStep: currentStep
})
}
previousButton() {
let currentStep = this.state.currentStep;
if(currentStep !==1){
return (
<button type="button" onClick={this._prev}>Назад</button>
)
}
return null;
}
nextButton(){
let currentStep = this.state.currentStep;
if(currentStep <3){
return (
<button type="button" onClick={this._next}>Вперед</button>
)
}
return null;
}
sendButton(){
let currentStep = this.state.currentStep;
if(currentStep === 3){
return (
<button>Отправить</button>
)
}
return null;
}
render() {
return (
<div>
<h1>Форма входа</h1>
<p>Шаг {this.state.currentStep} </p>
<form onSubmit={this.handleSubmit}>
<UserNameStep
currentStep={this.state.currentStep}
handleChange={this.handleChange}
username={this.state.username}
/>
<EmailStep
currentStep={this.state.currentStep}
handleChange={this.handleChange}
email={this.state.email}
/>
<PasswordStep
currentStep={this.state.currentStep}
handleChange={this.handleChange}
password={this.state.password}
/>
<p>
{this.previousButton()}
{this.nextButton()}
{this.sendButton()}
</p>
</form>
</div>
);
}
}
class UserNameStep extends React.Component {
render() {
if (this.props.currentStep !== 1) {
return null
}
return(
<div>
<label>Имя</label>
<input
name="username"
type="text"
placeholder="Введите имя"
value={this.props.username}
onChange={this.props.handleChange}
/>
</div>
);
}
}
class EmailStep extends React.Component {
render() {
if (this.props.currentStep !== 2) {
return null;
}
return(
<div>
<label>Email</label>
<input
name="email"
type="text"
placeholder="Введите email"
value={this.props.email}
onChange={this.props.handleChange}
/>
</div>
);
}
}
class PasswordStep extends React.Component {
render() {
if (this.props.currentStep !== 3) {
return null
}
return(
<div>
<label>Пароль</label>
<input
name="password"
type="password"
placeholder="Введите пароль"
value={this.props.password}
onChange={this.props.handleChange}
/>
</div>
);
}
}
ReactDOM.createRoot(
document.getElementById("app")
)
.render(<UserForm />);
</script>
</body>
</html>