The only way I know of is to create a new React Component which takes the value and the event handler as props.
This way, the handler as a function remains static, and since the value is passed down separately (in its own prop) you don't have any functions being re-instanciated. Because you don't bind anything nor create a new function each time.
Here's an example:
We have two buttons. The first one prints the current state variable value
and the other increments it by one.
Normally, if we had done this with onClick={() => this.print(this.state.value)}
we would get a new instance of this function, each time the MyApp
component would re-render. In this case, it would re-render each time we increment the value with the setState()
inside this.increment
.
However, in this example, no new instance of this.print
happens because we are only passing its reference to the button. In other words, no fat arrow and no binding.
In the <Button />
component, we have a <button>
to which event handler we pass a reference to a function - just like we did in <MyApp />
. However, here we know exactly what to pass to the function. As such, we have myHandler
trigger this.props.handler(this.props.value)
.
class MyApp extends React.Component {
constructor() {
super();
this.state = {
value: 0
};
}
print = (value) => {
console.log(value);
}
increment = () => {
// This will trigger a re-render, but none of the functions will be reinstanciated!
this.setState((prevState) => ({value: prevState.value + 1}));
}
render() {
// Note that both handlers below are passed just the references to functions. No "bind" and no fat arrow.
return(
<div>
<Button handler={this.print} value={this.state.value}>Print</Button>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
class Button extends React.Component {
// Clicking the button will trigger this function, which in turn triggers the function with the known argument - both of which were passed down via props.
myHandler = () => this.props.handler(this.props.value);
render() {
// Note again that the handler below is just given the reference to a function. Again, not "bind" nor fat arrow.
return(
<button onClick={this.myHandler}>{this.props.children}</button>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Though quite tedious, it is an effective solution. That being said, even if you do create a new function each time you render, the performance implications are minimal. From the official docs:
The problem with this syntax is that a different callback is created each time the LoggingButton
renders. In most cases, this is fine.