基于 React 的多步骤表单的魔力

Avatar of Nathan Sebhastian
Nathan Sebhastian

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

处理冗长、复杂的表单的一种方法是将其分解成多个步骤。例如,回答一组问题,然后继续下一组问题,然后再继续下一组,依此类推。我们通常将这些称为**多步骤表单**(原因显而易见),但也有人称之为“向导”表单。

多步骤表单可能是一个好主意!通过一次只显示几个输入,表单可能会显得更容易理解,并防止用户因大量的表单字段而感到不知所措。虽然我没有查过,但我敢说没有人喜欢填写一个巨大的表单——这就是多步骤表单派上用场的地方。

问题在于,多步骤表单——虽然减少了前端的感知复杂性——但在开发方面可能会让人感到复杂和难以驾驭。但是,我要告诉你,使用 React 作为基础,不仅可以实现它,而且相对简单。所以,这就是我们今天要一起构建的!

这是最终产品

让我们开始构建吧!

创建多步骤表单的最简单方法是创建一个容器表单元素,该元素包含所有步骤作为其内部组件。以下是一个可视化图表,显示了该容器()、其内部的组件()以及它们之间传递状态和属性的方式。

A diagram showing the master form component above three rectangles representing the three steps of the form, from left to right. Between the master form and the steps is a constructor, setState, and render.
充当容器,而其内部的三个子组件充当表单的每个步骤。

虽然它看起来比常规表单更复杂,但多步骤表单仍然使用与 React 表单相同的原理

  • 状态用于存储数据和用户输入。
  • 组件用于编写方法和界面。
  • 属性用于将数据和函数传递到元素中。

我们不会只有一个表单组件,而是会拥有一个组件和三个组件。在上图中,将通过属性将数据和函数发送到子组件,而子组件将触发handleChange()函数来设置状态中的值。它们形成一个和谐的大家庭!我们还需要一个函数来将表单从一个步骤移动到另一个步骤,稍后我们会讲到这一点。
步骤子(get it?)组件将从父组件接收valueonChange属性。

  • 组件将渲染一个电子邮件地址输入框
  • 将渲染一个用户名输入框
  • 将渲染一个密码输入框和一个提交按钮

将同时向子组件提供数据和函数,子组件将使用其props将用户输入传递回父组件。

创建步骤(子)组件

首先,我们将创建表单的子组件。在本例中,我们保持组件非常精简,每个步骤只使用一个输入,但每个步骤实际上可以像我们想要的那样复杂。由于子组件彼此之间看起来几乎相同,因此我在这里只展示其中一个。但是请务必查看演示以获取完整代码。

class Step1 extends React.Component {render() {
  if (this.props.currentStep !== 1) { // Prop: The current step
    return null
  }
  // The markup for the Step 1 UI
  return(
    <div className="form-group">
      <label htmlFor="email">Email address</label>
      <input 
        className="form-control"
        id="email"
        name="email"
        type="text"
        placeholder="Enter email"
        value={this.props.email} // Prop: The email input data
        onChange={this.props.handleChange} // Prop: Puts data into state
      />
    </div>
  )}
}

现在,我们可以将此子组件放入表单的render()函数中并传入必要的属性。就像在React 的表单文档中一样,我们仍然可以使用handleChange()将用户提交的数据与setState()一起放入状态中。handleSubmit()函数将在表单提交时运行。

接下来是父组件

让我们创建父组件——我们现在都知道,我们称之为——并初始化其状态和方法。

我们使用一个currentStep状态,它将初始化为默认值 1,表示表单的第一步()。随着表单的进行,我们将更新状态以指示当前步骤。

class MasterForm extends Component {
  constructor(props) {
    super(props)
    // Set the initial input values
    this.state = {
      currentStep: 1, // Default is Step 1
      email: '',
      username: '',
      password: '', 
    }

    // Bind the submission to handleChange() 
    this.handleChange = this.handleChange.bind(this)
  }

  // Use the submitted data to set the state
  handleChange(event) {
    const {name, value} = event.target
    this.setState({
      [name]: value
    }) 
  }

  // Trigger an alert on form submission
  handleSubmit = (event) => {
    event.preventDefault()
    const { email, username, password } = this.state
    alert(`Your registration detail: \n 
    Email: ${email} \n 
    Username: ${username} \n
    Password: ${password}`)
  }

  // Render UI will go here...
}

好的,这是我们正在寻找的基本功能。接下来,我们想要为实际的表单创建外壳 UI 并调用其中的子组件,包括将通过handleChange()传递的必需状态属性。

render() { 
  return (
    <React.Fragment>
      <h1>A Wizard Form!</h1>

      Step {this.state.currentStep} 
 
      <form onSubmit={this.handleSubmit}>

        // Render the form steps and pass in the required props
        <Step1 
          currentStep={this.state.currentStep} 
          handleChange={this.handleChange}
          email={this.state.email}
        />
        <Step2 
          currentStep={this.state.currentStep} 
          handleChange={this.handleChange}
          username={this.state.username}
        />
        <Step3 
          currentStep={this.state.currentStep} 
          handleChange={this.handleChange}
          password={this.state.password}
        /> 
      </form>
    </React.Fragment>
  )
}

一步一步来

到目前为止,我们允许用户填写表单字段,但我们没有提供任何实际方法来继续下一步或返回上一步。这就需要 next 和 previous 函数来检查当前步骤是否有前一步或下一步;如果有,则相应地向上或向下推动currentStep属性。

class MasterForm extends Component {
  constructor(props) {
    super(props)
    // Bind new functions for next and previous
    this._next = this._next.bind(this)
    this._prev = this._prev.bind(this)
  }
  // Test current step with ternary
  // _next and _previous functions will be called on button click
  _next() {
    let currentStep = this.state.currentStep
    // If the current step is 1 or 2, then add one on "next" button click
    currentStep = currentStep >= 2? 3: currentStep + 1
    this.setState({
      currentStep: currentStep
    })
  }

  _prev() {
    let currentStep = this.state.currentStep
    // If the current step is 2 or 3, then subtract one on "previous" button click
    currentStep = currentStep <= 1? 1: currentStep - 1
    this.setState({
      currentStep: currentStep
    })
  }
}

我们将使用一个get函数来检查当前步骤是 1 还是 3。这是因为我们有一个三步表单。当然,随着向表单中添加更多步骤,我们可以更改这些检查。我们还希望仅在实际上有下一步和上一步可以导航到时显示下一步和上一步按钮。

// The "next" and "previous" button functions
get previousButton(){
  let currentStep = this.state.currentStep;
  // If the current step is not 1, then render the "previous" button
  if(currentStep !==1){
    return (
      <button 
      className="btn btn-secondary" 
      type="button" 
      onClick={this._prev}>
          Previous
      </button>
    )
  }
  // ...else return nothing
  return null;
}

get nextButton(){
  let currentStep = this.state.currentStep;
  // If the current step is not 3, then render the "next" button
  if(currentStep <3){
    return (
      <button 
      className="btn btn-primary float-right" 
      type="button" 
      onClick={this._next}>
        Next
      </button> 
    )
  }
  // ...else render nothing
  return null;
}

剩下的就是渲染这些按钮了

// Render "next" and "previous" buttons
render(){
  return(
    <form onSubmit={this.handleSubmit}>
    {/* 
      ... other codes
    */}

    {this.previousButton}
    {this.nextButton}
    </form>
  )
}

恭喜,您现在是表单向导了!🧙

这是关于多步骤表单的多步骤教程中的最后一步。哇,多么元!虽然我们没有深入探讨样式,但希望这能为您提供关于如何使复杂表单不那么……复杂的有力概述!

这是最终演示的再次展示,以便您可以在其完整且辉煌的上下文中查看所有代码

考虑到 React 利用了状态、属性更改、可重用组件等,因此它非常适合这种事情。我知道 React 对某些人来说似乎门槛很高,但我写了一本书,让它变得更容易上手。我希望您能看看!