React 系列十:受控组件和非受控组件

快来加入咱们吧!

"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。html

"Code tailor" ,若是您对咱们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的联系,您也能够在微信上观看咱们的文章。每个建议或是赞同都是对咱们极大的鼓励!前端

前言

这节咱们将介绍 React 中受控组件和非受控组件的概念及其使用。react

本文会向你介绍如下内容:web

  • 什么是受控组件/非受控组件
  • 受控组件
  • 非受控组件

什么是受控组件/非受控组件

HTML 中,表单元素如 <input><textarea><select> 表单元素一般保持本身的状态,并根据用户输入进行更新。而在 React 中,可变状态通常保存在组件的 state(状态) 属性中,而且只能经过 setState() 更新。数组

咱们能够经过使 Reactstate 成为 “单一数据源原则” 来结合这两个形式。而后渲染表单的 React 组件也能够控制在用户输入以后的行为。浏览器

这种形式,其值由 React 控制的输入表单元素称为“受控组件”。微信

那么相反的,值并不禁 React 进行控制,该组件本身输入,减小等等,该元素成为非受控组件。markdown

关于何时使用受控组件,何时使用非受控组件,能够查看这一篇文章:app

Controlled and uncontrolled form inputs in React don't have to be complicated - Gosha Arinich (goshakkk.name)函数

受控组件

认识受控组件

默认提交表单方式

HTML 表单元素与 React 中的其余 DOM 元素有所不一样,由于表单元素天然地保留了一些内部状态。

例如,这个纯 HTML 表单接受一个单独的 name

<form>
  <label> 名字: <input type="text" name="name" /> </label>
  <input type="submit" value="提交" />
</form>
复制代码

该表单和 HTML 表单的默认行为一致,当用户提交此表单时浏览器会打开一个新页面。若是你但愿 React 中保持这个行为,也能够工做。

可是多数状况下,咱们会让 React 组件来管理这些数据,并在点击提交这些数据并触发打开新页面的操做。

这就用到了“受控组件(controlled components)”。

受控组件提交表单

HTML 中,表单元素(如<input><textarea><select>)之类的表单元素一般本身维护 state,并根据用户输入进行更新。

而在 React 中,可变状态(mutable state)一般保存在组件的 state 属性中,而且只能经过使用 setState()来更新。

  • 咱们将二者结合起来,使 Reactstate 成为“惟一数据源”;
  • 渲染表单的 React 组件还控制着用户输入过程当中表单发生的操做;
  • React 以这种方式控制取值的表单输入元素就叫作“受控组件”;

例如,若是咱们想让前一个示例在提交时打印出名称,咱们能够将表单写为受控组件:

class App extends PureComponent {
  constructor(props) {
    super(props)

    this.state = {
      username: '',
    }
  }

  render() {
    const { username } = this.state

    return (
      <div> <form onSubmit={(e) => this.handleSubmit(e)}> <label htmlFor="username"> 用户名: <input type="text" id="username" onChange={(e) => this.handleUsernameChange(e)} value={username} /> </label> <input type="submit" value="提交" /> </form> </div>
    )
  }

  handleUsernameChange(event) {
    this.setState({
      username: event.target.value,
    })
  }

  handleSubmit(event) {
    console.log(this.state.username)
    event.preventDefault()
  }
}
复制代码

因为在表单元素上设置了 value 属性,所以显示的值将始终为 this.state.value,这使得 React 的 state 成为惟一数据源。

因为 handleUsernameChange 在每次按键时都会执行并更新 Reactstate,所以显示的值将随着用户输入而更新。

非受控组件

Ref 的建立和使用

React 的开发模式中,一般状况下不须要、也不建议直接操做 DOM 原生,可是某些特殊的状况,确实须要获取到 DOM 进行某些操做:

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

咱们这里若是使用非受控组件就会有一个问题,如何获取该组件的数据,这里就能够用 Refs 来获取该组件,而后就能够获取到该组件的数据了。所以咱们先简述 Refs 的内容。

**注意:**固然还有其余的方法能够获取组件内容,例如:变量提高到父组件统一管理、事件监听。

建立 ref 的方式

如何建立 refs 来获取对应的 DOM 呢?目前有三种方式:

  • 方式一:传入字符串

使用时经过 this.refs.传入的字符串格式获取对应的元素;

  • 方式二:传入一个对象

对象是经过 React.createRef() 方式建立出来的;使用时获取到建立的对象其中有一个current属性就是对应的元素;

  • 方式三:传入一个函数

该函数会在 DOM 被挂载时进行回调,这个函数会传入一个 元素对象,咱们能够本身保存;使用时,直接拿到以前保存的元素对象便可;

代码演练:

class App extends PureComponent {
  constructor(props) {
    super(props)

    this.titleRef = createRef()
    this.titleEl = null
  }

  render() {
    return (
      <div> <h2 ref="title">String Ref</h2> <h2 ref={this.titleRef}>Hello Create Ref</h2> <h2 ref={(element) => (this.titleEl = element)}>Callback Ref</h2> <button onClick={(e) => this.changeText()}>改变文本</button> </div>
    )
  }

  changeText() {
    this.refs.title.innerHTML = '你好啊,小和山的菜鸟们'
    this.titleRef.current.innerHTML = '你好啊,小和山的菜鸟们'
    this.titleEl.innerHTML = '你好啊,小和山的菜鸟们'
  }
}
复制代码

Ref 节点的类型

ref 的值根据节点的类型而有所不一样:

  • ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 建立的 ref 接收底层 DOM 元素做为其 current 属性;
  • ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例做为其 current 属性;
  • 你不能在函数组件上使用 ref 属性,由于他们没有实例;

这里咱们演示一下 ref 引用一个 class 组件对象:

class Counter extends PureComponent {
  constructor(props) {
    super(props)

    this.state = {
      counter: 0,
    }
  }

  render() {
    return (
      <div> <h2>当前计数: {this.state.counter}</h2> <button onClick={(e) => this.increment()}>+1</button> </div>
    )
  }

  increment() {
    this.setState({
      counter: this.state.counter + 1,
    })
  }
}

class App extends PureComponent {
  constructor(props) {
    super(props)

    this.counterRef = createRef()
  }

  render() {
    return (
      <div> <Counter ref={this.counterRef} /> <button onClick={(e) => this.increment()}>app +1</button> </div>
    )
  }

  increment() {
    this.counterRef.current.increment()
  }
}
复制代码

函数式组件是没有实例的,因此没法经过 ref 获取他们的实例,可是某些时候,咱们可能想要获取函数式组件中的某个 DOM 元素,这个时候咱们能够经过 React.forwardRef ,后面咱们也会学习 hooks 中如何使用 ref

认识非受控组件

React 推荐大多数状况下使用受控组件来处理表单数据:

  • 一个受控组件中,表单数据是由 React 组件来管理的;
  • 另外一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理;

若是要使用非受控组件中的数据,那么咱们须要使用 Ref 来从 DOM 节点中获取表单数据。

咱们来进行一个简单的演练:

  • 使用 ref 来获取 input 元素;
  • 在非受控组件中一般使用 defaultValue 来设置默认值;
class App extends PureComponent {
  constructor(props) {
    super(props)

    this.usernameRef = createRef()
  }

  render() {
    return (
      <div> <form onSubmit={(e) => this.handleSubmit(e)}> <label htmlFor=""> 用户: <input defaultValue="username" type="text" name="username" ref={this.usernameRef} /> </label> <input type="submit" value="提交" /> </form> </div>
    )
  }

  handleSubmit(event) {
    event.preventDefault()
    console.log(this.usernameRef.current.value)
  }
}
复制代码

一样,<input type="checkbox"><input type="radio"> 支持 defaultChecked<select><textarea> 支持 defaultValue

下节预告

本节咱们学习了 React 中控组件和非受控组件的内容,在下一个章节咱们将继续学习 React 中高阶组件以及组件补充的内容,敬请期待!

相关文章
相关标签/搜索