Создание пошаговой формы

Последнее обновление: 23.03.2025

Пошаговые формы позволяют разбить ввод данных на отдельные этапы. Рассмотрим, как сделать подобную форму. Допустим, пользователь вводит сначала имя, потом 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>
);

Визуально это будет выглядеть следующим образом. Сначала отображается форма для ввода имени:

Multistep from in React

Далее по нажатию на кнопку происходит переход к форме ввода email:

Пошаговая форма ввода в React

И в конце отображается форма ввода пароля с кнопкой отправки, по нажатию на которую отображается окно с введенными данными:

Этапы ввода на форме в React

Аналогичный пример на компонентах-классах:

<!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>
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850