React 事件處理

React 元素的事件處理和 DOM 元素類似。但是有一點語法上的不同:

  • React 事件繫結屬性的命名採用駝峰式寫法,而不是小寫。
  • 如果採用 JSX 的語法你需要傳入一個函式作為事件處理函式,而不是一個字串(DOM 元素的寫法)

HTML 通常寫法是:

<button onclick="activateLasers()">
  啟用按鈕
</button>

React 中寫法為:

<button onClick={activateLasers}>
  啟用按鈕
</button>

在 React 中另一個不同是你不能使用返回 false 的方式阻止預設行為, 你必須明確使用 preventDefault

例如,通常我們在 HTML 中阻止連結預設開啟一個新頁面,可以這樣寫:
<a href="https://www.itread01.com/react/#" onclick="console.log('點選連結'); return false">
  點我
</a>

在 React 的寫法為:

function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('連結被點選'); } return ( <a href="#" onClick={handleClick}> 點我 </a> ); }

例項中 e 是一個合成事件。

使用 React 的時候通常你不需要使用 addEventListener 為一個已建立的 DOM 元素新增監聽器。你僅僅需要在這個元素初始渲染的時候提供一個監聽器。

當你使用 ES6 class 語法來定義一個元件的時候,事件處理器會成為類的一個方法。例如,下面的 Toggle 元件渲染一個讓使用者切換開關狀態的按鈕:

例項

class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // 這邊繫結是必要的,這樣 `this` 才能在回撥函式中使用 this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } } ReactDOM.render( <Toggle />, document.getElementById('example') );

嘗試一下 ?

你必須謹慎對待 JSX 回撥函式中的 this,類的方法預設是不會繫結 this 的。如果你忘記繫結 this.handleClick 並把它傳入 onClick, 當你呼叫這個函式的時候 this 的值會是 undefined。

這並不是 React 的特殊行為;它是函式如何在 JavaScript 中執行的一部分。通常情況下,如果你沒有在方法後面新增 () ,例如 onClick={this.handleClick},你應該為這個方法繫結 this。

如果使用 bind 讓你很煩,這裡有兩種方式可以解決。如果你正在使用實驗性的屬性初始化器語法,你可以使用屬性初始化器來正確的繫結回撥函式:

class LoggingButton extends React.Component { // 這個語法確保了 `this` 繫結在 handleClick 中 // 這裡只是一個測試 handleClick = () => { console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}> Click me </button> ); } }

如果你沒有使用屬性初始化器語法,你可以在回撥函式中使用 箭頭函式:

class LoggingButton extends React.Component { handleClick() { console.log('this is:', this); } render() { // 這個語法確保了 `this` 繫結在 handleClick 中 return ( <button onClick={(e) => this.handleClick(e)}> Click me </button> ); } }

使用這個語法有個問題就是每次 LoggingButton 渲染的時候都會建立一個不同的回撥函式。在大多數情況下,這沒有問題。然而如果這個回撥函式作為一個屬性值傳入低階元件,這些元件可能會進行額外的重新渲染。我們通常建議在建構函式中繫結或使用屬性初始化器語法來避免這類效能問題。


向事件處理程式傳遞引數

通常我們會為事件處理程式傳遞額外的引數。例如,若是 id 是你要刪除那一行的 id,以下兩種方式都可以向事件處理程式傳遞引數:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述兩種方式是等價的。

上面兩個例子中,引數 e 作為 React 事件物件將會被作為第二個引數進行傳遞。通過箭頭函式的方式,事件物件必須顯式的進行傳遞,但是通過 bind 的方式,事件物件以及更多的引數將會被隱式的進行傳遞。

值得注意的是,通過 bind 方式向監聽函式傳參,在類元件中定義的監聽函式,事件物件 e 要排在所傳遞引數的後面,例如:

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> {/* 通過 bind() 方法傳遞引數。 */} <a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a> </div> ); } }