问题
My friend and I are having an argument. In the interest of full disclosure, I'm the one who's a big fan of React and its benefits.
In React components, when attaching a DOM event to each element in a list of elements, the traditional pattern is to bind() the generic click handler with the values you want to pass along to that function as parameters. As written below:
<button onClick={this.onButtonClick.bind(this, buttonIndex)}></button>
where buttonIndex is some value that changes as the list of buttons is iterated over. This pattern allows onButtonClick to be generic, and expect buttonIndex as a parameter. Like so:
onButtonClick: function(buttonIndex) {
... some logic
}
My friend argues that this way of doing things is extremely inefficient. This requires that a new function be created and kept in memory to handle each button's event. I agree with his point, but I feel that the React devs wouldn't encourage this pattern in their docs, (at least twice) if the library didn't efficiently handle the events and their handlers.
The pattern he used to avoid this was to use the data- attribute and get the value (in this example, buttonIndex) off the DOM element itself:
<button data-buttonIndex={buttonIndex} onClick={this.onButtonClick}></button>
...
onButtonClick: function() {
var buttonIndex = $(this).data('buttonIndex');
...some logic
}
Again, I'm biased cus I'm the React fan. But this feels wrong, for two reasons:
- Getting values off the DOM to pass data around (as state) kinda defeats the purpose of React in a lot of ways, right?
data-attributes are extremely ambiguous in my opinion. They can be set from several different places (HTML, JS, PHP, etc.). And they don't suggest any implicit purpose. That "data" could be used anywhere, (JS, CSS, etc.)
Does React do some special magic to be efficent with its DOM events? And if not, is there an alternative pattern that doesn't use the data- attribute and is more explicit about its use?
回答1:
I'm not sure this is a good idea, but... memoize!
class Foo {
constructor(){
this.getClickHandler = _.memoize(this.getClickHandler);
}
getClickHandler(index){
return (event) => {
doSomething();
};
}
render(){
// ...
<button onClick={this.getClickHandler(index)}>Click me</button>
// ...
}
}
This way you avoid creating a new function, avoid data-attributes, and avoid the performance cost of looking up anything in the dom.
I don't think I've ever profiled and found creating functions in render to be an issue. This is definitely something you should optimize only when the numbers tell you to do so.
回答2:
I think in general binding functions directly in render is the idiomatic way because they do it in the docs as you pointed out and in our experience has not been significantly less performant. However, there are cases you don't want to rebind the function on every render, for example if you're comparing props in shouldComponentUpdate (like with PureRenderMixin). To do something very similar as your friend suggests but without looking at the DOM with jQuery (and I believe is a common pattern) you can pass the index as a prop.
class Parent extends React.Component {
render() {
return [...some array].map((item, index) => {
return <Child item={item} index={index} />;
});
}
}
class Child extends React.Component {
constructor() {
super();
this.handleClickButton = this.handleClickButton.bind(this);
}
handleClickButton() {
// use this.props.index here
}
render() {
return <button onClick={this.handleClickButton}></button>;
}
}
Note when using ES6 classes you need to bind to this manually in constructor since you're accessing this.props. You don't need to if you're using React.createClass. More about ES6 classes in React docs here.
来源:https://stackoverflow.com/questions/32366248/react-ineffiencies-of-binding-a-new-function-for-each-event