1. 程式人生 > >React-事件處理詳解

React-事件處理詳解

對於使用者介面而言,展示只佔整體設計因素的一半,另一半則是相應使用者輸入,即通過JavaScript處理使用者產生的事件。

React通過將事件處理器 繫結到組建上處理事件,事件觸發的同時更新組建的內部狀態,內部狀態更新會觸發元件的重繪。因此,如果檢視層想要渲染出事件出發後的結果,它所要做的就是渲染函式中讀取元件的內部狀態。

一、繫結事件處理器

React處理事件本身和原生的JavaScript事件一樣:MouseEvents事件用於點選處理器,Change事件用於表單元素變化,等等,所有事件在命名上與原生的JavaScript規範一致,並且會在相同的情境下被觸發,React繫結事件處理器的語法和HTML語法非常相似。比如,在我們的問卷製作工具例項中,包含了下面的程式碼,在Save按鈕上 繫結onclick事件處理器。

<button className ='btn btn-save'onClick={this.handleSaveClicked}>Save</button>

使用者點選這個按鈕時,元件的handleSaveClicked方法會被呼叫。這個方法中包含處理Save行為的邏輯。

PS:這個程式碼可在寫法上類似普通不推薦的HTML內聯事件處理器屬性,比如:onClick,但其實在底層實習那上冰沒有使用HTML的onClick屬性。React只是用這種寫法繫結事件處理器,但內部則是按照需要高效的維護著時間處理器。

如果不用JSX語法,你可以選擇在引數物件的屬性上指定事件處理器,比如:

React.DOM.button({ClassName:'btnbtn-save',onClick:this.handleSaveClicked},"Save");

其中大部分事件不需要而外的處理就可以工作,但是觸控事件需要通過呼叫一下程式碼手動啟動:

React.initializeTouchEvent(true);

二、事件和狀態

1、設想你需要讓一個元件隨著使用者的輸入而改變,比如在問卷編輯器中,你想要讓使用者從一個問題型別的選單當中拖拽問卷問題。

首先,在渲染函式內部基於HTML5拖放(Drag and Drop)API註冊事件處理器,程式碼如下:

var SurveyEdit =React.creatclass({
 

render:function(){
     <div className='survey-edit'>

<div className='row'>

<aside className='sidebar col-md-3'>

<h2>Module</h2>

<DraggableQuestions>

</aside>

<div className='survey-canvas col-md-9'>

    <div

className ={'drop-zone well well-drop-zone'}

onDragOver={this.handleDragOver}

onDragEnter={this.handleDragEnter}

onDragLeave={this.handleDragLeave}

onDrop ={this.handleDrop}

>

Drag and drop a module from the left

</div>

</div>

</div>

    </div>

},

});

三、根據狀態進行渲染

事件處理器方法還需要完成一件事——展開當前已經加入的題目清單。為了實現該功能,你需要充分利用每個React元件的內部狀態,元件預設是null,但是可以通過它的getInitialState方法將其出事話為合理的值,比如:

getInitaSate:funciton (){

return  {

dropZoneEntered:false,

title:’‘,

introduction:' ',

questions:{}

};

}

以上程式碼元件初始化了預設值:一個空標題、一個空的介紹、一組空的題目以及一個值為false的dropZoeEntered屬性,用於表示當前使用者沒有拖拽任何內容那個到放置區域。

到這裡,你已經可以在render方法當中讀取this.state,以便向用戶展示當前表單中所有資料了。

四、更新狀態

更新元件內部狀態會出發元件重繪,所以接下來要做的事情就是在拖拽的事件處理器方法中更新狀態,然後再次執行render函式,它會從this.state中讀取新資料來顯示標題,介紹及題目,使用者會看到所有內容被正確的更新。

更新元件狀態有兩種方案:元件的setState方法和repaceState方法。replaceState用一個全新的state物件完整的替換掉原有的state。使用不可變資料結構表示狀態時,這種方式很有效,不過很少應用用於其他場景下。更多的情況下回使用setState,它僅僅會把傳入的物件合併到已有的state物件。

例如:

getInitialState:funciton(){
      return {

dropZoneEntered:false,

title:'Fantastic Survey',

introduction:'This survey isfantastic!',

question :[]

};

}

這時,呼叫this.setState({title:"Fantasticsurvey 2.0"})僅僅影響this.setState.title的值,而this.state.dropZoneEntered、this.state.introduction及this.state.questions不會受影響。

而如果呼叫this.replaceState({title:"FantasticSurvey 2.0"}),則會用新的物件{title:“Fantastic Survey 2.0}替換掉整個state物件,同時把this.state.dropZone-Entered]this.state.introduction和this.state.questions全部清除掉。這樣做很可能中斷render函式執行,因為它期望this.state.questions是一個數組而不是undefined。現在使用this.setState可以實現上文中提到事件處理器方法。

使用this.State可以實現上文提到的事件處理器方法。

handleFormChange:function(formData){

this.setSate(formData);

}.

handleDragOver:function(ev){

//這保證 handleDroZoneDrop可以被呼叫

ev.preventDefault();

},

handleDragEnter:function(){

this.setState({dropZoneEntered:true});

},

handleDragLeave:function(){

this.setState({dropZoneEntered:false});

},

handleDrop:function(ev){

var questionType =ev.dataTransfer.getData('questionType');

var questions =this.state.questions;

questions = questions.concat({type:questionType});

this.setState({

questions:questions,

dropZoneEntered:false

});

});

 這一點很重要永遠不要嘗試通過setState或者replaceState以外的方法去修改state物件,類似this.state.saveInProgress =true通常不是一個好主意,因為它無法通知React師傅需要重新渲染元件,而且可能回答熬製下次呼叫setState時出現意外結果。

五、事件物件

很多事件處理器只要觸發就會完成功能,但有時也會需要關於使用者輸入的更多資訊。例如:

var AnswerEssayQuestion=React.createClass({

handleComplete:functin(event){

this.callMethodOnProps('onCompleted',event.target.value);

},

},

render:function(){
           return(

<div className="form-group">

<lavel className="survey-item-label">{this.props.label}</label>

<div className="survey-item-content">

<textarea className="form-control" rows="3"onBlur={this.handleComplete}/>

</div>

</div>

);

}

通常會有一個事件物件傳入到React的事件處理函式中,類似於原生的JavaScript事件監聽器的寫法。這裡的handleComplete方法會接受一個事件物件,並通過存取event.target.value值為textarea賦值,在事件處理器中,使用event.target.value獲取表單中input值是一個常規的方法,尤其在onClange事件處理器中。

注意:callMethodOnProps是由一個叫做PropsMethodMixin的mixin提供的,此外這個mixin還提供了一些處理父元件之間通訊的簡便辦法。

React把原生的事件封裝在一個SyntheticEvent例項中,而不是直接把原生的瀏覽器事件傳給處理器,SyntheticEvent在表現和功能上都與瀏覽器的原生事件一致,而且消除了跨瀏覽器差異。

對於使用者介面而言,展示只佔整體設計因素的一半,另一半則是相應使用者輸入,即通過JavaScript處理使用者產生的事件。

React通過將事件處理器 繫結到組建上處理事件,事件觸發的同時更新組建的內部狀態,內部狀態更新會觸發元件的重繪。因此,如果檢視層想要渲染出事件出發後的結果,它所要做的就是渲染函式中讀取元件的內部狀態。

一、繫結事件處理器

React處理事件本身和原生的JavaScript事件一樣:MouseEvents事件用於點選處理器,Change事件用於表單元素變化,等等,所有事件在命名上與原生的JavaScript規範一致,並且會在相同的情境下被觸發,React繫結事件處理器的語法和HTML語法非常相似。比如,在我們的問卷製作工具例項中,包含了下面的程式碼,在Save按鈕上 繫結onclick事件處理器。

<button className ='btn btn-save'onClick={this.handleSaveClicked}>Save</button>

使用者點選這個按鈕時,元件的handleSaveClicked方法會被呼叫。這個方法中包含處理Save行為的邏輯。

PS:這個程式碼可在寫法上類似普通不推薦的HTML內聯事件處理器屬性,比如:onClick,但其實在底層實習那上冰沒有使用HTML的onClick屬性。React只是用這種寫法繫結事件處理器,但內部則是按照需要高效的維護著時間處理器。

如果不用JSX語法,你可以選擇在引數物件的屬性上指定事件處理器,比如:

React.DOM.button({ClassName:'btnbtn-save',onClick:this.handleSaveClicked},"Save");

其中大部分事件不需要而外的處理就可以工作,但是觸控事件需要通過呼叫一下程式碼手動啟動:

React.initializeTouchEvent(true);

二、事件和狀態

1、設想你需要讓一個元件隨著使用者的輸入而改變,比如在問卷編輯器中,你想要讓使用者從一個問題型別的選單當中拖拽問卷問題。

首先,在渲染函式內部基於HTML5拖放(Drag and Drop)API註冊事件處理器,程式碼如下:

var SurveyEdit =React.creatclass({
 

render:function(){
     <div className='survey-edit'>

<div className='row'>

<aside className='sidebar col-md-3'>

<h2>Module</h2>

<DraggableQuestions>

</aside>

<div className='survey-canvas col-md-9'>

    <div

className ={'drop-zone well well-drop-zone'}

onDragOver={this.handleDragOver}

onDragEnter={this.handleDragEnter}

onDragLeave={this.handleDragLeave}

onDrop ={this.handleDrop}

>

Drag and drop a module from the left

</div>

</div>

</div>

    </div>

},

});

三、根據狀態進行渲染

事件處理器方法還需要完成一件事——展開當前已經加入的題目清單。為了實現該功能,你需要充分利用每個React元件的內部狀態,元件預設是null,但是可以通過它的getInitialState方法將其出事話為合理的值,比如:

getInitaSate:funciton (){

return  {

dropZoneEntered:false,

title:’‘,

introduction:' ',

questions:{}

};

}

以上程式碼元件初始化了預設值:一個空標題、一個空的介紹、一組空的題目以及一個值為false的dropZoeEntered屬性,用於表示當前使用者沒有拖拽任何內容那個到放置區域。

到這裡,你已經可以在render方法當中讀取this.state,以便向用戶展示當前表單中所有資料了。

四、更新狀態

更新元件內部狀態會出發元件重繪,所以接下來要做的事情就是在拖拽的事件處理器方法中更新狀態,然後再次執行render函式,它會從this.state中讀取新資料來顯示標題,介紹及題目,使用者會看到所有內容被正確的更新。

更新元件狀態有兩種方案:元件的setState方法和repaceState方法。replaceState用一個全新的state物件完整的替換掉原有的state。使用不可變資料結構表示狀態時,這種方式很有效,不過很少應用用於其他場景下。更多的情況下回使用setState,它僅僅會把傳入的物件合併到已有的state物件。

例如:

getInitialState:funciton(){
      return {

dropZoneEntered:false,

title:'Fantastic Survey',

introduction:'This survey isfantastic!',

question :[]

};

}

這時,呼叫this.setState({title:"Fantasticsurvey 2.0"})僅僅影響this.setState.title的值,而this.state.dropZoneEntered、this.state.introduction及this.state.questions不會受影響。

而如果呼叫this.replaceState({title:"FantasticSurvey 2.0"}),則會用新的物件{title:“Fantastic Survey 2.0}替換掉整個state物件,同時把this.state.dropZone-Entered]this.state.introduction和this.state.questions全部清除掉。這樣做很可能中斷render函式執行,因為它期望this.state.questions是一個數組而不是undefined。現在使用this.setState可以實現上文中提到事件處理器方法。

使用this.State可以實現上文提到的事件處理器方法。

handleFormChange:function(formData){

this.setSate(formData);

}.

handleDragOver:function(ev){

//這保證 handleDroZoneDrop可以被呼叫

ev.preventDefault();

},

handleDragEnter:function(){

this.setState({dropZoneEntered:true});

},

handleDragLeave:function(){

this.setState({dropZoneEntered:false});

},

handleDrop:function(ev){

var questionType =ev.dataTransfer.getData('questionType');

var questions =this.state.questions;

questions = questions.concat({type:questionType});

this.setState({

questions:questions,

dropZoneEntered:false

});

});

 這一點很重要永遠不要嘗試通過setState或者replaceState以外的方法去修改state物件,類似this.state.saveInProgress =true通常不是一個好主意,因為它無法通知React師傅需要重新渲染元件,而且可能回答熬製下次呼叫setState時出現意外結果。

五、事件物件

很多事件處理器只要觸發就會完成功能,但有時也會需要關於使用者輸入的更多資訊。例如:

var AnswerEssayQuestion=React.createClass({

handleComplete:functin(event){

this.callMethodOnProps('onCompleted',event.target.value);

},

},

render:function(){
           return(

<div className="form-group">

<lavel className="survey-item-label">{this.props.label}</label>

<div className="survey-item-content">

<textarea className="form-control" rows="3"onBlur={this.handleComplete}/>

</div>

</div>

);

}

通常會有一個事件物件傳入到React的事件處理函式中,類似於原生的JavaScript事件監聽器的寫法。這裡的handleComplete方法會接受一個事件物件,並通過存取event.target.value值為textarea賦值,在事件處理器中,使用event.target.value獲取表單中input值是一個常規的方法,尤其在onClange事件處理器中。

注意:callMethodOnProps是由一個叫做PropsMethodMixin的mixin提供的,此外這個mixin還提供了一些處理父元件之間通訊的簡便辦法。

React把原生的事件封裝在一個SyntheticEvent例項中,而不是直接把原生的瀏覽器事件傳給處理器,SyntheticEvent在表現和功能上都與瀏覽器的原生事件一致,而且消除了跨瀏覽器差異。