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 中高階組件以及組件補充的內容,敬請期待!

相關文章
相關標籤/搜索