"小和山的菜鳥們",爲前端開發者提供技術相關資訊以及系列基礎文章。爲更好的用戶體驗,請您移至咱們官網小和山的菜鳥們 ( xhs-rookies.com/ ) 進行學習,及時獲取最新文章。html
"Code tailor" ,若是您對咱們文章感興趣、或是想提一些建議,微信關注 「小和山的菜鳥們」 公衆號,與咱們取的聯繫,您也能夠在微信上觀看咱們的文章。每個建議或是贊同都是對咱們極大的鼓勵!前端
這節咱們將介紹 React
中受控組件和非受控組件的概念及其使用。react
本文會向你介紹如下內容:web
在 HTML
中,表單元素如 <input>
,<textarea>
和 <select>
表單元素一般保持本身的狀態,並根據用戶輸入進行更新。而在 React
中,可變狀態通常保存在組件的 state(狀態)
屬性中,而且只能經過 setState()
更新。數組
咱們能夠經過使 React
的 state
成爲 「單一數據源原則」 來結合這兩個形式。而後渲染表單的 React 組件也能夠控制在用戶輸入以後的行爲。瀏覽器
這種形式,其值由 React
控制的輸入表單元素稱爲「受控組件」。微信
那麼相反的,值並不禁 React
進行控制,該組件本身輸入,減小等等,該元素成爲非受控組件。markdown
關於何時使用受控組件,何時使用非受控組件,能夠查看這一篇文章:app
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()
來更新。
React
的 state
成爲「惟一數據源」;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
在每次按鍵時都會執行並更新 React
的 state
,所以顯示的值將隨着用戶輸入而更新。
在 React
的開發模式中,一般狀況下不須要、也不建議直接操做 DOM
原生,可是某些特殊的狀況,確實須要獲取到 DOM
進行某些操做:
DOM
庫。咱們這裏若是使用非受控組件就會有一個問題,如何獲取該組件的數據,這裏就能夠用 Refs
來獲取該組件,而後就能夠獲取到該組件的數據了。所以咱們先簡述 Refs
的內容。
**注意:**固然還有其餘的方法能夠獲取組件內容,例如:變量提高到父組件統一管理、事件監聽。
如何建立 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
屬性用於 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
中高階組件以及組件補充的內容,敬請期待!