React 組件中處理 onClick 類似事件綁定的時候,是需要顯式給處理器綁定上下文(context)的,這一度使代碼變得冗餘和難看。 請看如下的示例: class App extends Component { constructor() { super(); this.state = { i ...
React 組件中處理 請看如下的示例: class App extends Component { constructor() { super(); this.state = { isChecked: false }; } render() { return ( <div className="App"> <label > check me: <input type="checkbox" checked={this.state.isChecked} onChange={this.toggleCheck} /> </label> </div> ); } toggleCheck() { this.setState(currentState => { return { isChecked: !currentState.isChecked }; }); } } 頁面上放了一個 因為 出乎意料,是 WHY當然這並不是 React 的鍋,這是 JavaScript 中 預設的綁定function display(){ console.log(this) } display() // 嚴格模式下為全局 `window`,非嚴格模式下為 `undefined` 隱式綁定通過對象來調用,該函數的上下文被隱式地指定為該對象。 var obj = { name: 'Nobody', display: function(){ console.log(this.name); } }; obj.display(); // Nobody. 裡面取的是 obj 身上的 `name` 屬性 但,如果把該對象上的方法賦值給其他變數,或通過參數傳遞的形式,再執行,那光景就又不一樣了。 var obj = { name: "Nobody", display: function() { console.log(this.name); } }; var name = "global!"; var outerDisplay = obj.display; outerDisplay(); // global! 這裡取到的 `name` 是全局中的內個 這裡賦值給 function invoker(fn) { fn(); } setTimeout( obj.display, 1000 ); // global! invoker(obj.display); // global! 這裡 強制綁定這個時候, var name = “global!”; obj.display = obj.display.bind(obj); var outerDisplay = obj.display; outerDisplay(); // Nobody
現場還原有了上面的背景,就可以還原文章開頭的問題了,即事件處理器的上下文 丟失的問題。 JSX 中的 HTML 標簽本質上對應 React 中創建該標簽的一個函數。比如你寫的 React.createElement( type, [props], [...children] ) 標簽上的屬性會作為
React.createElement(type, props){ // 讓我們創建一個 <type> 併在 <type> 的值發生變化的時候調用一下 `props.onChange` ... props.onChange() // 它已經不是原來的方法了,丟失了上下文 ... } 因為 ES6 的 Class 是在嚴格模式下執行的,所以事件處理器中如果使用了 所以你看到 React 官方的示例中,constructor 里有 constructor() {
super();
this.state = {
isChecked: false
};
+ this.toggleCheck = this.toggleCheck.bind(this);
}
這樣是能正常工作了,但是,這句代碼的存在真的很彆扭,因為,
避免的方式有很多,就看哪種最對味。下麵來看看如何避免寫這些綁定方法。 #0行內的綁定最簡單的可以在行內進行綁定操作,這樣不用單獨寫一句出來。 <input type="checkbox" checked={this.state.isChecked} - onChange={this.toggleCheck} + onChange={this.toggleCheck.bind(this)} /> #1箭頭函數因為箭頭函數不會創建新的作用域,其上下文是語義上的(lexically)上下文。所以在綁定事件處理器時,直接使用剪頭函數是很方便的一種規避方法。 <input type="checkbox" checked={this.state.isChecked} - onChange={this.toggleCheck} + onChange={() => this.toggleCheck()} /> #2將類的方法改成屬性如果將這個處理器作為該組件的一個屬性,這個屬性作為事件的處理器以箭頭函數的形式存在,執行的時候也是能正常獲取到上下文的。 - toggleCheck() { + toggleCheck = () => { this.setState(currentState => { return { isChecked: !currentState.isChecked }; }); } 總結React 組件中,其實跟 React 沒多大關係,傳遞事件處理器,或方法作為回調時,其上下文會丟失。為了修複,我們需要顯式地給這個方法綁定一下上下文。除了常用的在構造器中進行外,還可通過箭頭函數,公有屬性等方式來避免冗餘的綁定語句。 相關資源 |