react中綁定點擊事件
by Charlee Li
通過李李
在React中綁定事件處理程序的最佳方法 (The best way to bind event handlers in React)
Binding event handlers in React can be tricky (you have JavaScript to thank for that). For those who know the history of Perl and Python, TMTOWTDI (There’s More Than One Way To Do It) and TOOWTDI (There’s Only One Way To Do It) should be familiar words. Unfortunately, at least for event binding, JavaScript is a TMTOWTDI language, which always makes developers confused.
在React中綁定事件處理程序可能很棘手(為此,您需要使用JavaScript)。 對于那些了解Perl和Python歷史的人來說,TMTOWTDI(不止一種方法)和TOOWTDI(只有一種方法)應該是熟悉的詞。 不幸的是,至少對于事件綁定,JavaScript是一種TMTOWTDI語言,它總是使開發人員感到困惑。
In this post, we will explore the common ways of creating event bindings in React, and I’ll show you their pros and cons. And most importantly, I will help you find the “Only One Way” — or at least, my favorite.
在本文中,我們將探討在React中創建事件綁定的常見方法,我將向您展示它們的優缺點。 最重要的是,我將幫助您找到“唯一的方式”,或者至少是我的最愛。
This post assumes that you understand the necessity of binding, such as why we need to do this.handler.bind(this)
, or the difference between function() { console.log(this); }
and () => { console.log(this)
; }. If you get confused about these questions, Saurabh Misra had an amazing post explaining them.
這篇文章假定您了解綁定的必要性,例如為什么我們需要執行this.handler.bind(this)
或function() { console.log(this); }
function() { console.log(this); }
和() => { console.log(this)
; }。 如果您對這些問題感到困惑,Saurabh Misra會給您一個驚人的解釋。
render()中的動態綁定 (Dynamic binding in render())
The first case commonly used is calling .bind(this)
in the render()
function. For example:
常用的第一種情況是在render()
函數中調用.bind(this)
。 例如:
class HelloWorld extends Component { handleClick(event) {} render() { return ( <p>Hello, {this.state.name}!</p> <button onClick={this.handleClick.bind(this)}>Click</button> ); }}
Of course this will work. But think about one thing: What happens if this.state.name
changes?
當然可以。 但是請考慮一件事:如果this.state.name
更改,會發生什么?
You might say that changing this.state.name
will cause the component to re-render()
. Good. The component will render to update the name part. But will the button be rendered?
您可能會說更改this.state.name
會導致組件重新render()
。 好。 組件將渲染以更新名稱部分。 但是按鈕會被渲染嗎?
Consider the fact that React uses the Virtual DOM. When render occurs, it will compare the updated Virtual DOM with the previous Virtual DOM, and then only update the changed elements to the actual DOM tree.
考慮一下React使用虛擬DOM的事實。 進行渲染時,它將比較更新的虛擬DOM與先前的虛擬DOM,然后僅將更改的元素更新為實際的DOM樹。
In our case, when render()
is called, this.handleClick.bind(this)
will be called as well to bind the handler. This call will generate a brand-new handler, which is completely different than the handler used when render()
was called the first time!
在我們的例子中,當調用render()
也會調用this.handleClick.bind(this)
綁定處理程序。 此調用將生成一個全新的處理程序 ,該處理程序與第一次調用render()
時使用的處理程序完全不同!
As in the above diagram, when render()
was called previously, this.handleClick.bind(this)
returned funcA
so that React knew onChange
was funcA
.
如上圖所示,當先前調用render()
時, this.handleClick.bind(this)
返回funcA
以便React知道onChange
是funcA
。
Later, when render()
is called again, this.handleClick.bind(this)
returned funcB
(note it returns a new function every time being called). This way, React knows that onChange
is no longer funcA
, which means that button
needs to be re-rendered.
稍后,當再次調用render()
時, this.handleClick.bind(this)
返回funcB
(請注意,每次調用它都會返回一個新函數)。 這樣,React知道onChange
不再是funcA
,這意味著需要重新渲染button
。
One button may not be a problem. But what if you have 100 buttons rendered within a list?
一個按鈕可能不是問題。 但是,如果列表中有100個按鈕呈現該怎么辦?
render() { return ( {this.state.buttons.map(btn => ( <button key={btn.id} onChange={this.handleClick.bind(this)}> {btn.label} </button> ))} );}
In the above example, any button label change will cause all the buttons to be re-rendered, since all buttons will generate a new onChange
handler.
在上面的示例中,任何按鈕標簽的更改都將導致所有按鈕被重新呈現,因為所有按鈕都將生成一個新的onChange
處理程序。
綁定在構造函數中 (Bind in constructor())
An old school way is to do the binding in the constructor. Nothing fancy:
一種古老的方法是在構造函數中進行綁定。 沒有什么花哨:
class HelloWorld extends Component { constructor() { this.handleClick = this.handleClickFunc.bind(this); } render() { return (<button onClick={this.handleClick}/>); }}
This way is much better than previous one. Calling render()
will not generate a new handler for onClick
, so the <butt
on> will not be re-rendered as long as the button does not change.
這種方法比以前的方法好得多。 調用render()
不會為onClick
生成新的處理程序,因此只要按鈕不發生變化, <butt
on>就不會重新呈現。
與箭頭功能綁定 (Bind with the Arrow Function)
With ES7 class properties (currently supported with Babel), we can do bindings at the method definition:
使用ES7類屬性(當前受Babel支持),我們可以在方法定義處進行綁定:
class HelloWorld extends Component { handleClick = (event) => { console.log(this.state.name); } render() { return (<button onClick={this.handleClick}/>) }}
In the above code, handleClick
is an assignment which is equivalent to:
在上面的代碼中, handleClick
是等效于:
constructor() { this.handleClick = (event) => { ... };}
So once the component is initialized, this.handleClick
will never change again. This way, it ensures that <butt
on> won’t get re-rendered. This approach is probably the best way of doing bindings. It’s simple, easy to read, and most importantly, it works.
因此,一旦組件初始化, this.handleClick
將不再更改。 這樣,可以確保<butt
on>不會被重新渲染。 這種方法可能是進行綁定的最佳方法。 它簡單,易于閱讀,最重要的是,它可以工作。
使用箭頭功能動態綁定多個元素 (Dynamic binding with the Arrow Function for multiple elements)
Using the same arrow function trick, we can use the same handler for multiple inputs:
使用相同的arrow函數技巧,我們可以對多個輸入使用相同的處理程序:
class HelloWorld extends Component { handleChange = name => event => { this.setState({ [name]: event.target.value }); } render() { return ( <input onChange={this.handleChange('name')}/> <input onChange={this.handleChange('description')}/> ) }}
At first glance, this looks pretty amazing due to its simplicity. However, if you consider carefully, you’ll find that it has the same problem as the first approach: every time render()
is called both<inp
ut> will be re-rendered.
乍看之下,由于其簡單性,這看起來非常驚人。 但是,如果仔細考慮,您會發現它與第一種方法存在相同的問題:每次調用render()
,兩個<inp
ut>都將被重新渲染。
Indeed I do think this approach is smart, and I don’t want to write multiple handleXXXChange
for each field either. Luckily, this type of “multi-use handler” is less likely to appear inside a list. This means that there will be only a couple of <inp
ut> components that get re-rendered, and there probably won’t be a performance issue.
確實,我確實認為這種方法很聰明,而且我也不想為每個字段編寫多個handleXXXChange
。 幸運的是,這種類型的“多用途處理程序”不太可能出現在列表中。 這意味著將只重新渲染幾個<inp
ut>組件,并且可能不會出現性能問題。
Anyway, the benefits it brings to us are much greater than the performance loss. Therefore, I would suggest that you use this approach directly.
無論如何,它給我們帶來的好處遠大于性能損失。 因此,我建議您直接使用此方法。
In case those performance issues becoming significant, I would suggest caching the handlers when doing the bindings (but this will make the code less readable):
如果這些性能問題變得很重要,我建議在進行綁定時將處理程序緩存(但這會使代碼的可讀性降低):
class HelloWorld extends Component { handleChange = name => { if (!this.handlers[name]) { this.handlers[name] = event => { this.setState({ [name]: event.target.value }); }; } return this.handlers[name]; } render() { return ( <input onChange={this.handleChange('name')}/> <input onChange={this.handleChange('description')}/> ) }}
結論 (Conclusion)
When doing event bindings in React, we must check very carefully whether the handlers are generated dynamically. Usually this is not a problem when the affected components appear only once or twice. But when event handlers appear in a list, this can results in severe performance issues.
在React中進行事件綁定時,我們必須非常仔細地檢查處理程序是否動態生成。 通常,當受影響的組件僅出現一次或兩次時,這不是問題。 但是,如果事件處理程序出現在列表中,則可能導致嚴重的性能問題。
解決方案 (Solutions)
- Use Arrow Function binding whenever possible 盡可能使用箭頭功能綁定
- If you must generate bindings dynamically, consider caching the handlers if the bindings become a performance issue 如果必須動態生成綁定,請考慮在綁定成為性能問題時緩存處理程序
Thanks for reading! I hope this post was helpful. If you find this post useful, please share it with more people by recommending it.
謝謝閱讀! 希望這篇文章對您有所幫助。 如果您發現此帖子有用,請通過推薦與更多人分享。
Update:
更新:
Omri Luzon and Shesh mentioned lodash-decorators
and react-autobind
packages for more convenient bindings. Personally I am not a big fan of automatically doing anything (I am always trying to keep things such bindings minimal) but auto bind is absolutely a great way of writing clean code and saving more efforts. The code would be like:
Omri Luzon和Shesh提到了lodash-decorators
和react-autobind
包,以實現更方便的綁定。 就我個人而言,我并不喜歡自動執行任何操作(我一直在努力使綁定等內容最小化),但是自動綁定絕對是編寫簡潔代碼并節省更多精力的好方法。 代碼如下:
import autoBind from 'react-autobind';class HelloWorld() { constructor() { autoBind(this); }
handleClick() { ... } render() { return (<button onClick={this.handleClick}/>); }}
Since autoBind
will handle the bindings automatically, it is not necessary to use arrow function trick ( handleClick = () =>
{} ) to do the binding, and in the rende
r() function, this.handleCl
ick can be used directly.
由于autoBind
會自動處理的綁定,則沒有必要使用箭頭功能特技( handleClick = () =>
{})做的結合,并在噸he rende
R()函數的n, this.handleCl
可以使用ICK直。
翻譯自: https://www.freecodecamp.org/news/the-best-way-to-bind-event-handlers-in-react-282db2cf1530/
react中綁定點擊事件