第8章 DOM操作
多數情況下,React的虛擬DOM足以用來創建你想要的用戶體驗,而根本不需要直接操作底層真實的DOM。然而也有一些例外。最常見的場景包括:需要與一個沒有使用React的第三方類庫進行整合,或者執行一個React沒有原生支持的操作。
訪問受控的DOM節點
想要訪問React控制的DOM節點,首先必須能夠訪問到負責這些DOM的組件。這可以通過為子組件添加一個ref屬性來實現。
render () {return (<canvas ref="mainCanvas" />)
}
這樣就可以通過this.refs.mainCanvas
訪問到<canvas
組件。
??必須保證賦給每個子組件的ref值在所有子組件中是唯一的;否則操作就會失效。
可以通過getDOMNode()方法訪問到底層的DOM節點。
??不可以在render方法中這樣做。因為這時底層的DOM可能不是最新的(甚至尚未創建)
盡管refs和getDOMNode很強大,但請在沒有其他的方法能夠實現你需要的功能時再去選擇它們。使用它們會成為React在性能優化上的障礙,并且增加應用的復雜性。
componentDidMount方法只會為每個DOM節點調用一次。
如果在componentDidMount方法內導致了DOM節點無法被移除,有可能導致內存泄漏或者其他的問題。如果你擔心這一點,請在componentWillUnmount監聽器,用于在組件的DOM節點移除時清理它自身。
第9章 表單
在React中,表單組件有兩種類型:約束組件和無約束組件
無約束組件
給定一個HTML的<input/>
一個值,它的值是可以改變的。這正是無約束組件名稱的由來,因為表單組件的值是不受React控制的。
submitHandler = (event) => {event.preventDefault();var hellTo = this.refs.hellTo.getDOMNode().value;alert(hellTo)
}
render () {return (<form onSubmit={this.submitHandler}><input ref="hellTo" type="text" defaultValue="Hello World!" /><button type="submit">Speak</button></form>)
}
無約束組件可以用在基本的無需任何驗證或者輸入控制的表單中。
約束組件
約束組件,表單的狀態交由React組件控制,狀態值被存儲在React組件的state中。
constructor(props) {super(props);this.state = {helloTo = 'Hello World!'}
}
handleChange = (event) => {this.setState({helloTo: event.target.value})
}
submitHandler = (event) => {event.preventDefault();alert(this.state.helloTo)
}
render () {return (<form onSubmit={this.submitHandler}><input ref="hellTo" type="text" onChange={this.handleChange} /><button type="submit">Speak</button></form>)
}
表單事件
React支持所有HTML事件。這些事件遵循駝峰命名的約定,且會被轉成合成事件。
所有合成事件都提供了event.target來訪問觸發事件的DOM節點。
Label
由于for是JavaScript的保留字,所以我們無法把它作為一個對象的屬性。
jsx
<label htmlFor="name">Name:</label>
javascript
React.DOM.label({htmlFor:"name", "Name"});
渲染后:
<label for="name">Name:</label>
文本框和Select
React對<textarea/>
和<select/>
的接口做了一些修改,提升了一致性,讓它們操作起來更容易。
<textarea/>
被改的更像<input/>
了,允許我們設置value和defaulteValue。
//非約束的
<textareadefaultValue ="HelloWorld" />
//約束的
<textareavalue={this.state.helloTo } onChange ={this.handleChange} />//非約束的
<selectdefaultValue="8"><option value="A">First Option</option><option value="B">Second Option</option><option value= "C">Third Option</option>
</select>
//約束的
<selectvalue ={this.state.helloTo} onchange={this.handleChange}><option value="A">FirstOption </option><option value="B">Second Option </option><option value="C">Third Option</option>
</select>
React支持都選selce他,需要給value的defauletValue傳遞一個數組,如:defaultValue={["A","B"]}。
當使用可多選的select時,select組件的值在選項被選擇時不會更新,只有選項的selected屬性會發生變化。你可以使用ref或者syntheticEvent.target來訪問選項,檢查他們是否被選中。
下面的例子中,handleChange循環檢查DOM,并過濾出哪些選項被選中了。
class Hello extends React.Component {constructor(props) {super(props);this.state = {option: ["B"]};}
handleChange = (event) => {var checked = [];var sel = event.target;for (var i = 0; i < sel.length; i++){var option = sel.options[i];if (option.selected){checked.push(option.value);}}this.setState({option: checked});}
submitHandler = (event) => {event.preventDefault();alert(this.state.options);}render () {return(<form onSubmit={this.submitHandler}><select multiple="true" value={this.state.options}onChange={this.handleChange}><option value="A">1</option><option value="B">2</option><option value="C">3</option></select><br /><button type="submit">speak</button></form>);}
}
復選框和單選框
復選框和單選框使用的則是完全不同的控制方式。
在HTML中,類似為checkbox或者radio的<input/>
的行為完全不一樣,通常,復選框或者單選框的值是不變的,只有checked的狀態會變化,要控制復選框或者單選框,就要控制他們的checked屬性,你要可以在非約束的復選框或者單選框中使用defaultChecked。
//非約束的
var MyForm = React.createClass({submitHandler: function(event){event.preventDefault();alert(this.refs.checked.getDOMNode().checked);},render: function(){return (<form onSubmit={this.submitHandler}><input ref="checked" type="checkbox" value="A" defaultChecked="true" /><br /><button type="submit">speak</button></form>);}
});//約束的
var MyForm = React.createClass({getInitialState: function(){return {checked: true};},handleChange: function(event){this.setState({checked: event.target.checked});},submitHandler: function(event){event.preventDefault();alert(this.state.checked);},render: function(){return (<form onSubmit={this.submitHandler}><input type="checkbox" value="A" checked={this.state.checked} onChange={this.handleChange} /><br/><button type="submit">speak</button></form>);}
});
在這兩個例子中,<input/>
的值一直都是A,只有checked的狀態在變化。
表單元素的name屬性
在React中,name屬性對于表單元素來說并沒有那么重要。因為約束表單組件已經把值存儲到了state中,并且表單的提交事件也會被攔截。在獲取表單值的時候,name屬性并不是必需的。對于非約束的表單組件來說,也可以使用refs來直接訪問表單元素。雖然如此,name仍然是表單組件中非常重要的一部分。
- name屬性可以讓第三方表單序列化類庫在React中正常工作。
- 對于任然使用傳統提交方式的表單來說,name屬性是必需的。
- 在用戶的瀏覽器中,name被用在自動填寫常用信息中,比如用戶地址等。
- 對于非約束的單選框組件來講,name是有必要得,它可作為這些組件分組的依據。確保在同一時刻,同一表單中擁有同樣name的單選框只有一個可以被選中。如果不使用name屬性,這一行為可以使用約束的單選框實現。
多表單與change處理器
在使用約束的表單組件時,沒有愿意重復地為每一個組件編寫change處理器。可以在React中重用一個事件處理器。
可以有兩種方式:通過.bind傳遞其他參數;使用DOMNode的name屬性來判斷需要更新哪個組件的狀態。
示例可以看page75~77
除此之外,React還在addon中提供了一個mixin,React.addons.LinkedStateMixin,通過另一種方式解決同樣的問題。
React.addons.LinkedStateMixin為組件提供了一個linkState方法。linkState返回一個對象,包含value和requestChange兩個屬性。
value根據提供的name屬性從state中獲取對應的值。
requestChange是一個函數,使用心得值更新同名的state。
mixins:[React.addons.LinkedStateMixin]
submitHandler = (event) => {event.preventDefault();alert(this.state.family_name);
}<input type="text" name="family_name" valueLink={this.linkState('family_name')} />
這種方法便于控制表單域,把其值保存在福組件的state中。而且,其數據流仍然與其他約束的表單元素保持一致。
但是,使用這種方式往數據流中添加定制功能時,復雜度會增加。我們建議只在特性的場景下使用。因為傳統的約束表單組件提供了同樣的功能而且更加靈活。
自定義表單組件
當編寫自定義組件時,接口應當與其他表單組件保持一致。這可以幫助用戶理解代碼,明白如何使用自定義組件,且無須深入到組件的實現細節里。
示例:page79~82
Focus
React實現了autoFocus屬性,因此在組件第一次被掛載時,如果沒有其他的表單聚焦時React就會把焦點放到這個組件對應的表單域中,例如:
<input type="text" name="given_name"autoFocus="true" />
還有一種方法就是調用DOMNode的focus方法,手動設置表單域聚焦。
可用性
React雖然提高了開發者的生產力,但是也有不盡如人意的地方,使用React編寫的組件常常缺乏可用性,例如表單提交無法通過鍵盤敲擊回車鍵來實現,而這明明是HTML表單默認的提交方式。
要編寫具有高可用性的好組件其實也不難,只是編寫組件時需要花時間進行更多的思考。
- 把要求傳達清楚
- 不斷地反饋
- 迅速響應
- 符合用戶的預期
- 可訪問
- 減少用戶的輸入