MartinDuo MartinDuo - 2 months ago 11
React JSX Question

Binding event handlers in React Components fires onClick when rendered

I am learning React. In a test app I'm writing, I am rendering some buttons with onClick methods. When they are rendered like this, they work and call the selectMode function as expected when clicked.

constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
}
...
selectMode(mode) {
this.setState({ mode });
}

render() {
...
return (<div>
<button onClick={this.selectMode.bind(this, 'commits')}>Show Commits</button><br/>
<button onClick={this.selectMode.bind(this, 'forks')}>Show Forks</button><br/>
<button onClick={this.selectMode.bind(this, 'pulls')}>Show Pulls</button>
</div>
)
}


But when I tried the suggested best practices way shown below, by binding in the constructor, the selectMode function is called three times when the component is rendered. Why are the onClick event handlers being called then? What do I have wrong?

constructor(props) {
super(props);
this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
this.selectMode = this.selectMode.bind(this)
}
...
selectMode(mode) {
this.setState({ mode });
}

render() {
...
return (<div>
<button onClick={this.selectMode('commits')}>Show Commits</button><br/>
<button onClick={this.selectMode('forks')}>Show Forks</button><br/>
<button onClick={this.selectMode('pulls')}>Show Pulls</button>
</div>
)
}

Answer

your this.selectMode(...) is executed IMMEDIATELY whenever your component is rendered.

<.. onClick={this.selectMode('commits')}..../> <-- function is called immediately

You can use arrow function to create an anonymous function in which you can call your method. In this way, you this.selectMode method only get called when the click event occurs :

<.. onClick={() => this.selectMode('commits')}..../> 

If you don't want to create anonymous functions everytime you render the component, you can store an value to an attribute of the element. Like this :

constructor(props) {
 super(props);
 this.state = { mode: 'commits', commits: [], forks: [], pulls: [] };
 this.selectMode = this.selectMode.bind(this)
}
selectMode(event){
    this.setState({mode: e.target.name});
}
render(){
....
   <.. onClick={this.selectMode} name='commits' ..../> 
..}