事件處理
React元素的事件處理和DOM元素的事件處理很相似,但是有一點語法上的不同:
- React事件綁定屬性的命名采用駝峰式寫法,而不是小寫(DOM元素寫法)。
- 如果采用了JSX的語法,需要傳入一個函數(shù)作為事件處理函數(shù),而不是一個字符串(DOM元素寫法)
比如:傳統(tǒng)的HTML:
<button onclick="activateLasers()">Activate Lasers</button>
React中寫法:
<button onClick={activateLasers}>Activate Lasers</button>
可以看到,傳統(tǒng)HTML中點擊事件的屬性名是小寫的onclick,而React中是駝峰式的onClick;點擊事件的值,傳統(tǒng)的HTML中是一個字符串,字符串的值就是處理函數(shù)的名稱:activateLasers(),而React中采用的JSX語法,點擊事件的值就是這個函數(shù)名稱:activateLasers。
- React(從版本v0.14開始)中不能使用返回false的方式組織默認行為。必須使用事件的preventDefault函數(shù),傳統(tǒng)的HTML就可以直接返回false。
比如,傳統(tǒng)的HTML組織鏈接默認打開一個新頁面:
<a onclick="console.log('The line was clicked');return false">
Click me </a>
這樣點擊鏈接Click me時,會在控制臺輸出The line was clicked,但是不會打開新的url地址https://react.docschina.org/docs/handling-events.html。
在React中,直接返回false是不行的。需要顯示調(diào)用出發(fā)事件的preventDefault函數(shù):
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The line was clicked');
}
return (
<a onClick=
{handleClick} >Click me </a>
);
}
ReactDOM.render(
<ActionLink />, document.getElementById('root')
);
這里是使用函數(shù)式組件演示的,可以直接拷貝代碼在這里測試。記得把上邊的代碼copy到JS框中,如下圖:

上面是React使用函數(shù)組件來實現(xiàn)的,如果使用類組件,則如下:
class ActionLink extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
console.log('The line was clicked');
}
render () {
return (<a onClick={this.handleClick}>Click me </a>);
};
}
ReactDOM.render(
<ActionLink />, document.getElementById('root')
);
結(jié)果如下:

在這里,e是一個合成事件。React根據(jù)W3C spce來定義這些合成事件。更多事件參考SyntheticEvent 。
使用React的時候通常不需要為DOM元素添加監(jiān)聽器,如使用addEventListener方法。僅僅需要在元素初始化的時候提供一個監(jiān)聽器。
我們也看到了,使用函數(shù)組件和類組件,事件處理函數(shù)在語法上有點區(qū)別。使用函數(shù)組件的時候,事件處理函數(shù)還是像普通的函數(shù)一樣聲明function handleClick() {},但是使用類組件的時候,時間處理函數(shù)就是類的一個方法,所以在定義的時候是handleClick(){},沒有使用function關(guān)鍵字了。
我們還發(fā)現(xiàn),在使用類組件的時候,在構(gòu)造函數(shù)中有這么一句代碼:
this.handleClick = this.handleClick.bind(this);
在render方法的返回元素中,onClick屬性的值,我們使用的是JSX語法:onClic={this.handleClick},也就是點擊事件的處理函數(shù)是this的handleClick方法,this代表的是當前組件。也就是點擊事件其實是回調(diào)的this的handleClick方法,我們必須謹慎對待JSX回調(diào)函數(shù)中的this,類的方法默認是不會綁定this的,如果沒有如上的綁定代碼,即沒有綁定this.handleClick,那么在handleClick這個方法中使用this的話,就會報錯,this提示是undefined:如下:

這并不是React的特殊行為,他是函數(shù)如何在JavaScript中運行的一部分。如果沒有在方法后面添加(),例如onClick={this.handleClick},那就需要在constructor方法中為handleClick這個方法綁定this。當然可以在后邊機上(),但是這樣在初始化的時候就直接執(zhí)行了,達不到我們想要的效果,圖下圖:

如果不想使用綁定,還有另外兩種解決方式:
- 1.使用實驗性的屬性初始化器語法
class ActionLink extends React.Component {
handleClick = () => {
console.log(this);
}
render() {
return (<a href="#" onClick={this.handleClick}>click me</a>);
}
}
如下圖:

- 2.在回調(diào)函數(shù)中使用箭頭函數(shù)
class ActionLink extends React.Component{
handleClick(e) {
console.log(e);
console.log(this);
}
render(){
return (<a href="#" onClick={(e) => this.handleClick(e)}></a>);
}
}
如下圖:

注意:使用這個語法有個問題,每次組件有變化需要重新渲染的時候,都會創(chuàng)建一個不同的回調(diào)函數(shù)。在多數(shù)情況下,沒有問題。但是如果這個回調(diào)函數(shù)作為一個屬性值傳入低階組件,那么這些組件可能會進行額外的重新渲染。
向事件處理程序傳遞參數(shù)
通常我們會給事件處理程序傳遞額外的參數(shù),進行不同的操作。例如,若是id是我們需要刪除的那一行數(shù)據(jù)的id,那么以下兩種穿殘方式度可以:
<a onClick={(e) => this.handleClick(id, e)}>Delete Row</a>
<a onClick={this.handleClick.bind(this, id)}> Delete Row</a>
上述兩種方法是等價的。但是需要注意的是,在使用箭頭函數(shù)arrow functions的形式進行回調(diào)的時候,參數(shù)e作為React時間對象是作為第二個參數(shù)進行傳遞的,也就是時間獨享必須顯示的進行傳遞。但是通過bind的方式,事件對象以及更多的參數(shù)是被隱式的進行傳遞的。
但是通過bind方式向時間處理程序傳遞參數(shù)的時候,如果在事件處理程序中使用到了事件對象本身e,那么在函數(shù)定義的時候,e必須是作為參數(shù)列表的最后一個參數(shù):
class Popper extends React.Component{
constructor(){
super();
this.state = {name:'Hello world!'};
}
preventPop(name, e){ //事件對象e要放在最后
e.preventDefault();
alert(name);
}
render(){
return (
<div>
<p>hello</p>
{/* Pass params via bind() method. */}
<a onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
</div>
);
}
}