React 中的受控组件以及非受控组件

216 阅读3分钟

这篇文章是为了区别题述两种组件的应用场景以及一些细节。

受控组件

React的官方文档
针对表单,据文档中的原话:“html表单元素在 HTML 中,表单元素(如<input>、 <textarea> 和 <select>)通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。”
综合以上两种情景,如果让React的state成为表单的唯一数据源,渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
一个简单的方法判断。在使用一个受控组件时,要传入一个值,还要传入当这个值变化时操作这个值的方法,一般为onChange方法。
例如,如果我们想让前一个示例在提交时打印出名称,我们可以将表单写为受控组件。
下面代码示例是将两种input封装成Input组件。在父组件Input中使用html表单元素input。

class Input extends React.Component {
  constructor() {
    super()
    this.state = {
      value: '',
      checked: false
    }
  }

  onChange = ({target:{value}}) => {
    this.setState({ value })
  }

  onSubmit = (e) => {
    console.log('提交的值为:', this.state.value)
    e.preventDefault()
  }

  render() {
    return (
      <form onSubmit={this.onSubmit}>
        <label>
          Text:
          <input type="text" value={this.state.value} onChange={this.onChange} /> 
        </label>         
        <label>
          CheckBox:
          <input type="checkbox" checked={this.state.checked} onChange={({target: {checked}}) => (this.setState({ checked }))} />         
        </label>         
          <input type="submit" value="Submit" />
      </form>    
    )
  }
}

使用form、input时需要传入value的值以及自定义的onChangeonSubmit方法。

<form onSubmit={this.onSubmit}>

<input type="text" value={this.state.value} onChange={this.onChange} />

<input type="checkbox" checked={this.state.checked} onChange={({target: {checked}}) => (this.setState({ checked }))} />

因此,就把这个例子中的这些html表单元素称为受控组件。

将这个例子在codesandbox中运行。 点击submit即可拿到用户的输入数据。

image.png 由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 onChange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。
这种组件的使用需要对数据的每一种变化都给出相应的方法,在数据变化种类多样时这种方法会显得很复杂。这也就引出了非受控组件

非受控组件

要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。

class Input extends React.Component {
  constructor() {
    super()
    this.myInput = React.createRef()
  }

  handleSubmit = (event) => {
    console.log('提交的姓名为:', this.myInput.current.value)
    event.preventDefault()
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.myInput} />         
        </label>         
          <input type="submit" value="Submit" />
      </form>     )
  }
}

能实现和受控组件同样的功能,但是它的数据是输入后通过ref操作DOM获取,进而渲染在页面中。这种情况下不能通过操作外部值来改变。

还可以指定一个 defaultValue 属性,而不是 value。在一个组件已经挂载之后去更新 defaultValue 属性的值,不会造成 DOM 上值的任何更新。

总结

受控组件,简单来讲,就是受我们控制的组件,组件的状态全程响应外部数据。受控组件我们一般需要传入一个value和一个状态更新事件函数。
非受控组件,简单来讲,就是不受我们控制的组件。一般情况是在初始化的时候接受外部数据以默认值defaultValue来呈现,然后自己在内部存储其自身状态。当需要时,可以使用ref 查询 DOM并查找其当前值,

参考文献