原文地址:React Forms: Using Refscss
原文做者:Loren Stewarthtml
譯者:萌萌前端
校對者:小 boyreact
React 提供了兩種從 <form>
元素中獲取值的標準方法。第一種方法是實現所謂的受控組件 (能夠看我博客裏發表的文章) ,第二種方法是使用 React 的 ref
屬性。git
受控組件很重,被展現的值和組件的 state 綁定是它的特性。咱們經過執行一個附着在 form 元素上的 onChange
事件句柄,來更新被展現的值。onChange
函數更新 state 屬性,進而更新 form 元素的值。github
(在看到下面的文章以前,若是你只是想看相應的示例代碼:請移步這裏)api
受控組件示例:數組
import React, { Component } from 'react'; class ControlledCompExample extends Component { constructor() { super(); this.state = { fullName: '' } } handleFullNameChange = (e) => { this.setState({ fullName: e.target.value }) } handleSubmit = (e) => { e.preventDefault(); console.log(this.state.fullName) } render() { return ( <div> <form onSubmit={this.handleSubmit}> <label htmlFor="fullName">Full Name</label> <input type="text" value={this.state.fullName} onChange={this.handleFullNameChange} name="fullName" /> <input type="submit" value="Submit" /> </form> </div> ); } } export default ControlledCompExample;
input 的值是 this.state.fullName
(在第7行和第26行)。 onChange
函數是 handleFullNameChange
(第 10 - 14 行和第 27 行)。函數
受控組件最主要的優點是:
一、便於驗證用戶的輸入
二、能夠根據受控組件的值動態地渲染其餘組件。例如:一個用戶在下拉列表中選擇的值(如「dog」 或者 「cat」 )能夠控制在 form 中渲染的其餘 form 組件(例如:一個設置品種的複選框)this
受控組件的缺點是要寫大量的代碼。你須要經過 props 把 state 屬性傳遞給 form 元素,還須要一個函數來更新這個屬性的值。
對於單一表單元素來講這真的不是什麼問題 —— 可是若是你須要一個龐大而且複雜的表單(不須要動態渲染或者實時驗證),過分使用受控表單會讓你書寫成噸的代碼。
從 form 元素取值的簡便的方法是使用 ref
屬性。咱們用不一樣的方式來應對不一樣的 form 元素和組件結構,因此這篇文章剩下的內容分爲如下幾個部分。
使用 ref
的最簡單的例子是文本和數字 input 元素。咱們在 input 的 ref
屬性裏添加一個把 input 自己做爲參數的箭頭函數。我喜歡把參數命名爲和元素自己同樣的的名字,就像下面的第三行那個樣子:
<input type="text" ref={input => this.fullName = input} />
因爲該參數是 input 元素自己的別名,你能夠爲所欲爲地爲它命名:
<input type="number" ref={cashMoney => this.amount = cashMoney} />
接着你能夠拿到該參數,並將它賦值給當前 class 內 this
關鍵字上掛載的屬性(譯者注:這裏的 class 指的是 JSX 所處的 React 組件 class)。input(例如: DOM 節點)能夠經過 this.fullName
和 this.amount
來讀取。它的值能夠經過 this.fullName.value
和 this.amount.value
來讀取。
選擇元素也能夠用相同的方法(例如:下拉列表)。
<select ref={select => this.petType = select} name="petType"> <option value="cat">Cat</option> <option value="dog">Dog</option> <option value="ferret">Ferret</option> </select>
選擇元素的值能夠經過 this.petType.value
獲取。
經過受控組件,父組件獲取子組件的值十分簡單 —— 父組件中已經有這個值了(譯者注:在父組件中定義)!它被傳遞給子組件。同時 onChange
方法也被傳給子組件,用戶經過與 UI 互動(譯者注:觸發 onChange
)來更新該值。
你能夠在我上篇文章的受控組件示例中看到它是如何運行的。
雖然該值已經存在於受控組件的父組件中,可是當使用 ref
的時候卻不是這樣。使用 ref
的時候,該值存在於 DOM 節點自身當中,必須向上與父組件通訊。
要將該值從子組件傳給父組件,父組件須要向子組件傳遞一個 鉤子
。而後子組件將節點掛載到 鉤子
上, 以便父組件讀取。
在咱們更深刻的探討以前先來看一些代碼。
import React, { Component } from 'react'; class RefsForm extends Component { handleSubmit = (e) => { e.preventDefault(); console.log('first name:', this.firstName.value); this.firstName.value = 'Got ya!'; } render() { return ( <div> <form onSubmit={this.handleSubmit}> <CustomInput label={'Name'} firstName={input => this.firstName = input} /> <input type="submit" value="Submit" /> </form> </div> ); } } function CustomInput(props) { return ( <div> <label>{props.label}:</label> <input type="text" ref={props.firstName}/> </div> ); } export default RefsForm;
經過上面的代碼,能夠看到一個 form 組件 RefForm
和一個叫作 CustomInput
的 input 組件。一般,箭頭函數都是在 input 自身上面,可是從這(15 - 27 行)能夠看到它是經過 props 傳遞的。因爲箭頭函數存在於父組件中,因此 this.firstName
中的 this
指向父組件。
input 子組件的值被賦給父組件的 this.firstName
屬性,因此父組件能夠得到子組件的值。如今,父組件中的 this.firstName
指的是子組件中的 DOM 節點(例如: CustomInput
中的 input)。
父組件不只能夠訪問 input 中的 DOM 節點,還能夠在父組件內給節點的值賦值。在上文的第 7 行能夠看到例子。一旦表單被提交, input 的值就被設置爲 「Got ya!」 。
這種方式有點讓人摸不着頭腦,因此請仔細揣摩並敲代碼實踐一下,直至徹底理解。
你可能會寫出來更好的 radio 和 checkbox 受控組件,可是若是你真的想要用 `ref` ,那麼接下來的兩部分會幫到你。
不像 text 和 number 這類 input 元素,radio 元素是成組出現的。每組中的元素都有相同的 name
屬性,就像這樣:
<form> <label> Cat <input type="radio" value="cat" name="pet" /> </label> <label> Dog <input type="radio" value="dog" name="pet" /> </label> <label> Ferret <input type="radio" value="ferret" name="pet" /> </label> <input type="submit" value="Submit" /> </form>
在 「pet」 radio 標籤集合中有三個選項 —— 「cat」、「dog」 和 「ferret」。
因爲咱們關心的是整個集合的元素,因此給每一個單選框設置 ref
並非一個好主意。遺憾的是,沒有 DOM 節點是包含了 radio 集合的。
能夠經過下面的三步來檢索出 radio 集合的值:
一、在 form
標籤上設置 ref (下面的第20行)。
二、從 form 中取出這個 radio 集合。而後它應該是 pet
集合(下面的第9行)。
此處返回一個節點列表和一個值。在這種狀況下,這個節點列表包含三個 input 節點和被選中的值。
須要注意的是這個節點列表是個類數組,它沒有數組的方法。在下一部分中還有更多關於這個話題的內容。
三、使用 .
方法來獲取這個集合的值(下面的第13行)。
import React, { Component } from 'react'; class RefsForm extends Component { handleSubmit = (e) => { e.preventDefault(); // 從 form 中取出節點列表 // 它是一個類數組,沒有數組的方法 const { pet } = this.form; // radio 標籤集合有 value 屬性 // 查看打印出來的數據 console.log(pet, pet.value); } render() { return ( <div> <form onSubmit={this.handleSubmit} ref={form => this.form = form}> <label> Cat <input type="radio" value="cat" name="pet" /> </label> <label> Dog <input type="radio" value="dog" name="pet" /> </label> <label> Ferret <input type="radio" value="ferret" name="pet" /> </label> <input type="submit" value="Submit" /> </form> </div> ); } } export default RefsForm;
若是你正在用子組件寫一個表單也是可行的。儘管組件中會有更多的邏輯,可是從 radio 集合中獲取值的方法是不變的。
import React, { Component } from 'react'; class RefsForm extends Component { handleSubmit = (e) => { e.preventDefault(); // 從 form 中取出節點列表 // 它是一個類數組,沒有數組的方法 const { pet } = this.form; // radio 標籤集合有 value 屬性 // 查看打印出來的數據 console.log(pet, pet.value); } render() { return ( <div> <form onSubmit={this.handleSubmit} ref={form => this.form = form}> <RadioSet setName={'pet'} setOptions={['cat', 'dog', 'ferret']} /> <input type="submit" value="Submit" /> </form> </div> ); } } function RadioSet(props) { return ( <div> {props.setOptions.map(option => { return ( <label key={option} style={{textTransform: 'capitalize'}}> {option} <input type="radio" value={option} name={props.setName} /> </label> ) })} </div> ); } export default RefsForm;
和 radio 標籤集合不同, Checkbox 標籤集合可能有多個值。致使獲取這些值會比獲取 radio 標籤集合的值難一些。
能夠經過下面的五步來檢索出 checkbox 標籤集合被選中的值:
一、在 form
標籤上設置 ref (下面的第27行)。
二、從 form 中取出這個checkbox 集合。而後它應該是 pet
集合(第9行)。
此處返回一個節點列表和一個值
須要注意的是這個節點列表是一個類數組,它沒有數組的方法,而後咱們就須要進行下面的這一步 ...
三、把這個節點列表轉換成一個數組,而後就可使用數組的方法了(第 12 行的 checkboxArray
)。
四、使用 Array.filter()
獲取選中的 checkbox (第 15 行的 checkedCheckboxes
)。
五、使用 Array.map()
獲取選中的 checkbox 的惟一的值(第 19 行的 checkedCheckboxesValues
)
import React, { Component } from 'react'; class RefsForm extends Component { handleSubmit = (e) => { e.preventDefault(); // 從 form 中取出節點列表 // 它是一個類數組,沒有數組的方法 const { pet } = this.form; // 把節點列表轉換成一個數組 const checkboxArray = Array.prototype.slice.call(pet); // 僅取出被選中的 checkbox const checkedCheckboxes = checkboxArray.filter(input => input.checked); console.log('checked array:', checkedCheckboxes); // 使用 .map() 方法從每一個被選中的 checkbox 中把值取出來 const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value); console.log('checked array values:', checkedCheckboxesValues); } render() { return ( <div> <form onSubmit={this.handleSubmit} ref={form => this.form = form}> <label> Cat <input type="checkbox" value="cat" name="pet" /> </label> <label> Dog <input type="checkbox" value="dog" name="pet" /> </label> <label> Ferret <input type="checkbox" value="ferret" name="pet" /> </label> <input type="submit" value="Submit" /> </form> </div> ); } } export default RefsForm;
使用子組件寫 checkbox 的方法和上一部分中寫 radio 的方法是同樣的。
import React, { Component } from 'react'; class RefsForm extends Component { handleSubmit = (e) => { e.preventDefault(); // 從 form 中取出節點列表 // 它是一個類數組,沒有數組的方法 const { pet } = this.form; // 把節點列表轉換成一個數組 const checkboxArray = Array.prototype.slice.call(pet); // 僅取出被選中的 checkbox const checkedCheckboxes = checkboxArray.filter(input => input.checked); console.log('checked array:', checkedCheckboxes); // 使用 .map() 方法從每一個被選中的 checkbox 中把值取出來 const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value); console.log('checked array values:', checkedCheckboxesValues); } render() { return ( <div> <form onSubmit={this.handleSubmit} ref={form => this.form = form}> <CheckboxSet setName={'pet'} setOptions={['cat', 'dog', 'ferret']} /> <input type="submit" value="Submit" /> </form> </div> ); } } function CheckboxSet(props) { return ( <div> {props.setOptions.map(option => { return ( <label key={option} style={{textTransform: 'capitalize'}}> {option} <input type="checkbox" value={option} name={props.setName} /> </label> ) })} </div> ); } export default RefsForm;
若是你不須要:
一、實時監視 form 元素的值(例如:爲了基於用戶的輸入渲染以後的組件)
二、實時執行自定義驗證方法
那麼使用 ref
方法獲取 form 元素的值是一個很好的方法。
大多數狀況下,越過受控組件使用 ref
最主要的價值是會寫更少的代碼。 checkbox ( radio 其次)是一個特例。對於 checkbox ,使用 ref
省下的代碼量是不多的,因此沒法說是使用受控組件好仍是 ref
好。
iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、噹噹開售。