浏览器太低级了

守口如瓶,笑脸相迎。叩首问路,码梦为生。 --Vace

React 中受控和非受控的表单的区别以及场景

2021/11/06    前端


React 有两种不同的方式来处理表单输入。

如果一个 input 表单元素的值是由 React 控制,就其称为受控组件。当用户将数据输入到受控组件时,会触发修改状态的事件处理器,这时由你的代码来决定此输入是否有效(如果有效就使用更新后的值重新渲染)。如果不重新渲染,则表单元素将保持不变。

一个非受控组件,就像是运行在 React 体系之外的表单元素。当用户将数据输入到表单字段(例如 input,dropdown 等)时,React 不需要做任何事情就可以映射更新后的信息。然而,这也意味着,你无法强制给这个表单字段设置一个特定值。

在很多文章中说不应该使用setStateref,甚至有些理论自相矛盾,很难理解如何正确的处理react中的表单,甚至没有相对统一的标准。

到底应该怎么做表单呢?毕竟表单是许多web应用程度的核心,然而在react中处理表单,总有些不舒服,比如受控模式(controlled)和非受控模式(uncontrolled)的使用场景区别。

非受控模式(uncontrolled)

不受控输入和传统的HTML表单差不多,在提交或者使用时再去读取他的值,例如:

class Form extends Component {
  constructor (props) {
    super(props)
    this.input = React.createRef()
  }
  onSubmitClick: () => {
    alert(this.input.current.value)
  }

  render () {
    return (
      <div>
        <input type="text" ref={this.input} />
        <button onClick={this.onSubmitClick}>登陆用户</button>
      </div>
    )
  }
}

换句话说,必须在需要的时候,从dom中读取出字段值,比如提交表单的时候,这是实现表单输入的最简单方法,不过这个功能,在一些场景中可能不太合适,所以接下来我们看看受控模式下的表单。

注意:在非受控组件中,经常期望赋予组件一个初始值,但是后续不去更新,这种情况下可指定一个defaultValue,在一个组件已经挂载之后,是更新defaultValue的值,不会造成dom上的任何更新

function AppRender () {
  const input = React.createRef()
  const onHandleSubmit = () => {
    alert(input.current.value)
  }
  return (
    <form onSubmit={onHandleSubmit}>
      <label>
        Name:
        <input
          defaultValue="Bob"
          type="text"
          ref={input} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  )
}
元素 默认值
<input type="checkbox"> defaultChecked
<input type="radio"> defaultChecked
<input> defaultValue
<textarea> defaultValue
<select> defaultValue

受控模式(controlled)

一个受控输入组件会接收当前的值作为prop,使用回调函数改变这个值,或者说是一种更react的方式。

<input value={someValue} onChange={onHandleChange}>

通常表单输入的值会保存在当前组件的state上,也可以在另外的组件state上,甚至是redux之类的单独状态存储器中。

class Form extends Components {
  constructor (props) {
    super(props)
    this.state = {
      name: ''
    }
  }

  onHandleName (e) {
    this.setState({ name: event.target.value  })
  }

  render () {
    return (
      <div>
        <input
          type="text"
          value={this.state.name}
          onChange={this.onHandleName}
        />
      </div>
    )
  }
}

👆🏻上面的案例中每次输入新的字符是,都会实时通过onChange更新state

  • 从一个空字符串开始 - state = {name: ''}
  • 输入a时,value="a" - setState({ name: 'a' })
  • 输入b时,value="ab" - setState({ name: 'ab' })

这种流程会将表单值的变更推送到Form组件,所以Form组件始终具有当前输入的值,这意味着数据state和UIinput始终保持同步,状态为输入提供值,输入要求Form更改当前值,这意味着表单组件可以立即响应更改数据,例如:

  • 反馈表单状态,如输入验证
  • UI状态与输入相关,如未填写必填字段时,禁用按钮
  • 强制输入特定格式数据,如手机号、信用卡号的输入

如果应用中不需要这些,那么使用受控组件和非受控组件区别不大。

如何使元素受控?

除了基础的input元素,表单还包含其他的各类元素,如selecttextarea,如果通过prop设置表单元素的值,则表单将被控制,为受控组件,就是这么简单。

但是不同的表单元素都有不同的属性来设置该值:

元素 value 更改回调 回调中的新值
<input type="text" /> value="string" onChange event.target.value
<input type="checkbox" /> checked={boolean} onChange event.target.checked
<input type="radio" /> checked={boolean} onChange event.target.checked
<textarea /> value="string" onChange event.target.value
<select /> value="option value" onChange event.target.value

文件输入File

在 HTML 中,<input type="file"> 可以让用户选择一个或多个文件上传到服务器,或者通过使用 File API 进行操作。

<input type="file" />

在 React 中,<input type="file" /> 始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制。

总结

受控组件和非受控表单组件都具有有点,应该根据具体场景进行选择,如果UI反馈非常简单,则无需关注这些问题

特征 不受控制的 受控
一次性值(如提交时)
提交时验证
实时数据验证
有条件地禁用提交按钮
强制输入格式
一个数据的多个输入
动态输入

总结

官方 React 文档上的表单页面 官方 React 文档上的支持事件 Controlled and uncontrolled form inputs in React don’t have to be complicated

评论加载中...