react響應和處理事件

React 使用最多的,是設計應用的設計UI,而用戶界面一個最重要的目的是以有效的方式響應用戶輸入。用戶輸入多種多樣,有鼠標點擊,有鍵盤按鍵,有窗口大小的更改,如果運行在手機上的話,還得響應拖拽等手勢操作。

應對這些複雜的情形的辦法是抽象,把用戶的任何操作都當做一種事件,如果你瞭解原來的web開發的話,那麼你一定知道,很多開發的精力都要放在響應DOM事件的處理之上。

做一個簡單的程序(把頁面顯示給用戶),有一個按鈕,上面是一個數字,點擊一次按鈕,數字就增加1.麻雀雖小五腑俱全,這個應用實例看起來簡單,但很實用,每次你點擊按鈕時,一個點擊事件就會產生,在代碼中監聽這個事件,然後依賴 React提供給我們的各種機制,響應該事件,然後讓顯示的數值加一

例:

var Counter = React.createClass({
    render: function() {
         var textStyle= {
             fontSize:72,
             fontFamily:"sans-serif",
             color: "#333",
             fontWeight:"bold"
         };
         return (
             <div style={textStyle}>
                {this.props.display}
             </div>
        );
    }
});
var CounterParent = React.createClass({
     getInitialState:function() {
             return {
                 count: 0
             };
         },
         render: function() {
             var backgroundStyle ={
                 padding:50,
                 backgroundColor:"#FFC53A",
                 width: 250,
                 height:100,
                 borderRadius:10,
                 textAlign: "center"
             };
             var buttonStyle ={
                 fontSize:"1em",
                 width:30,
                 height:30,
                 fontFamily:"sans-serif",
                 color: "#333",
                 fontWeight:"bold",
                 lineHeight: "3px"
             };
             return (
                 <div style={backgroundStyle}>
                    <Counter display={this.state.count}/>
                        <button style={buttonStyle}>+</button>
                 </div>
            );
    }
});
ReactDOM.render(
     <div>
        <CounterParent/>
     </div>,
     destination
);

創建了兩個組件,Counter用來顯示計數信息,CounterParent是Counter組件的父組件,它負責繪製黃色背景以及加號按鈕,同時時,在顯示計數組件時,傳入要顯示的數值信息,該數值信息存儲在組件的state對象中

當前點擊加號按鈕時,組件沒有任何響應,接下來實現按鈕點擊後的響應功能,需要做的是響應按鈕的點擊事件,一旦事件發生

後,增加this.state.count的值,把增加後的值傳遞給Counter組件。

JSX代碼片段中,明確的添加要監聽的事件,並設置事件的響應處理函數,在CounterParent組件的實現代碼中,添加以下代

碼:

increase:function(e) {
     this.setState({
         count: this.state.count+1
     });
 },

render: function() {
...
return  (
         <div style={backgroundStyle}>
            <Counter display={this.state.count}/>
            <button onClick={this.increase} style={buttonStyle}>+
            </button>
         </div>
         );
...
}

添加上面代碼後,點擊加號按鈕,數字就能根據點擊的次數做相應的更改了。

事件屬性

在increase函數的實現中,它傳入了一個變量e,這個變量就是事件對象,該對象包含有很多屬性變量,不同事件,裏面包含的內 容還不一樣,例如,如果事件是鼠標點擊後發生的,那麼e的對象類型就是MouseEvent, 這個對象裏就能夠包含很多與鼠標相關 的信息,例如鼠標按下時,被點擊的是左鍵還是右鍵,如果事件是由鍵盤發出的,那麼事件的類型就是KeyboardEvent, 該對象包含的屬性能顯示很多鍵盤相關的信息,例如我們能得知,鍵盤上那一按鈕被按下了。

然而,上面談到的事件對象是DOM產生的,這些事件對象在進入React框架後,會被React進行二次處理,React 會把他們封裝成 一個事件對象叫SyntheticEvent, 它包含以下屬性: (把這部分先拷貝到編輯器) 屬性 類型

bubbles boolean cancelable boolean currentTarget DOMEventTarget defaultPrevented boolean eventPhase boolean isTrusted boolean nativeEvent DOMEvent preventDefault() void isDefaultPrevented() boolean isPropagationStopped void target DOMEventTarget timeStamp umber type string

以上屬性是事件對象的固定部分,對不同的事件,SyntheticEvent對象還會封裝不同屬性,例如對應MouseEvent 對象,那麼 還得封裝下列屬性:

boolean altKey number button number buttons number clientX number clientY boolean ctrlKey boolean getModifierState(key) number pageX number pageY number screenX number screenY boolean shiftKey

如果DOM發出的是KeyboardEvent對象,那麼SyntheticEvent將會 封裝下列屬性: boolean altKey number charCode boolean ctrlKey boolean getModifierState(key) string key number keyCode string locale number location boolean metaKey boolean repeat boolean shiftKey number which

接下來看看如何利用SytheticEvent對象裏面的相關屬性, 例如,如果想知道一個按鍵按下時,是不是shfit鍵也按下 了,那麼只要查詢shiftKey 這個屬性是不是true就可以了 。於是在事件響應函數中,根據傳進來的對象,讀取它的 shiftKey 屬性,如果該屬性是true, 那麼表明shift鍵被按下, 於是在增加計數時,一下子增加10:

increase:function(e) {
     var currentCount = this.state.count;
     if (e.shiftKey){
         currentCount += 10;
     } else {
        currentCount +=  1;
     }
     this.setState({
         count: currentCount
     });
}

添加上面代碼後,如果你在用鼠標點擊按鈕時,按下鍵盤的shift鍵,那麼計數會一下子增加10,如果沒有按,那麼計數只會每次添加1

事件處理的注意事項
現在看到的知識React事件處理機制的表面,有很多深層次的要點,還沒有了解。真正的web應用,要處理的很多事件比我們當前例子要複雜很多,必須掌握更多React的事件處理技巧,以便滿足開發需要。
接下來看看在實際開發中有可能要遇到的各種問題,以及相應的處理辦法。
首先要知道,React組件不能直接監聽事件

在例子中,監聽按鈕點擊事件的辦法是在button控件中添加要監聽的事件名字,並提供事件的響應函數,但如果面對的並非html標準控件,而是我們自己定義的React組件,那麼,我們不能使臫當前方式去監聽和響應事件

例如我們定義了一個控件叫PlusButton, 那麼下面這麼做是錯誤的:

var PlusButton = React.createClass({
    render: function() {
        return  (
            <button>+</button
        );
    }
});
<PlusButton onClick={this.increase}/>
從表面上看,這句代碼邏輯上沒什麼問題,但是組件對html標準控件的封裝和延伸,對標準控件進行事件監聽的方式,不能直接應用到組件上。因爲React 無法區別這種做法到底是監聽事件還是給組件屬性賦值。

但這不是說,如果標準控件被React組件封裝後就不能從組件外層實現事件監控了,需要繞個彎去解決這個問題,例如,我們可以把事件的響應函數當做屬性從外面傳給組件,例如看以下代碼:

var PlusButton = React.createClass({
    render: function() {
        return (
             <button onClick={this.props.clickHandler} style={this.props.showStyle}>
                            +
             </button>
             );
    }
});                 
var CounterParent = React.createClass({
    ....
    render: function() {
        var  backgroundStyle ={
             padding:50,
             backgroundColor:"#FFC53A",
             width: 250,
             height:100,
             borderRadius:10,
             textAlign: "center"
        };
        var buttonStyle = {
             fontSize:"1em",
             width:30,
             height: 30,
             fontFamily: "sans-serif",
             color: "#333",
             fontWeight:"bold",
             lineHeight:"3px"
        };
        return  (
            <div style={backgroundStyle}>
                <Counter display={this.state.count}/>
                <PlusButton clickHandler={this.increase} 
                showStyle={buttonStyle}/>
            </div>
        );
    }
    ....
}

上面代碼修改後,點擊按鈕,計數功能仍然正常運行。html標準控件定義在PlusButton組件內部,先在button控件上監聽onClick事件,而事件的響應函數則從控件外部當做屬性傳遞進去

this 關鍵字在事件處理函數中的指向

必須要注意,在事件響應函數中,如果你用到關鍵字this的話,這個this指代的是定義響應函數的組件對象,例如在CountParent中,如果我們在increase函數中把this打印出來:

increase: function(e) {
    console.log("CounterParent: "   +   this);
    var currentCount = this.state.count;
    if (e.shiftKey){
        currentCount +=  10;
    } else {
        currentCount+=  1;
    }
    this.setState({
        count:currentCount
    });
},
添加上面代碼,運行後,在瀏覽器的控制檯觀察輸出信息,可以發現,打印出的this對象內容描述的是組件CounterParent.

React框架會把this綁定到執行函數所屬的組件對象,但是這種自動綁定只有在組件是通過createClass API創建時才行。

React的事件響應機制如此設計是出自於一些考量的: 首先考慮的是瀏覽器的兼容性,其次要考慮的是事件處理的效率。

React 把所有事件對象封裝到SyntheticEvent這個對象裏,是爲了兼容舊版本的瀏覽器。封裝了後,在React開發過程中,就不必要考慮瀏覽器的不同版本,然後編寫不同邏輯的代碼,因此React大大簡化了事件處理的開發邏輯。其次,當設計的用戶界面越複雜,需要響應的事件越多,那麼手動實現各種事件響應的代碼邏輯就會越來越複雜,爲了應對這種情況,React並不是機械的去處理各個不同的事件,在低下它事件上使用的是 一個總的事件響應函數來捕捉DOM產生的所有事件,先做相應處理,然後再分發給對應的響應函數,通過這種方式,React能夠優化事件的分發流程,進而提高事件的響應效率

結論: 本節,深入瞭解了React的事件處理機制,先掌握了基本的事件監聽和響應放法,然後,又研究了在一些特殊情況下,如何實現事件響應,通過本節,對React的事件處理機制應該會有較爲深刻的認識。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章