MartinDuo
MartinDuo

Reputation: 753

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>
        )
} 

Upvotes: 1

Views: 1313

Answers (2)

Phi Nguyen
Phi Nguyen

Reputation: 3056

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' ..../> 
..}

Upvotes: 5

Kevin
Kevin

Reputation: 1377

I'm not sure, but I think it's because you call upon each onClick function by adding the parentheses. If you use ES6 you could try doing this:

onClick = () => { this.selectMode('commits') }
onClick = () => { this.selectMode('forks') }
onClick = () => { this.selectMode('pulls') }

Upvotes: 1

Related Questions