0

Im making a react component that creates multiple buttons but I dont know how to handle the action depending on which button is pressed, this is the component:

var SingleChoiceGroup = React.createClass({
    render(){
        var numberOfButtons = this.props.numberOfButtons;
        var prefix = this.props.prefix;
        var buttons = [];

        for(var i = 0;i<numberOfButtons;i++){
            buttons.push(
                <Button value={i} onClick={() => this.props.selectedItem(i)}>{prefix + " " + (i+1)}</Button>
            );
        }

        return(
            <div>
                {buttons}
            </div>
        )
    }
});

And this is the method were I want to get the parameter:

var AnotherComponent = React.createClass({
    selectedDay(i){
        // Here I want to read the index parameter from the other component.
    },

    render(){
        <SingleChoiceGroup selectedItem={() => this.selectedDay()} numberOfButtons={7} prefix={"Text"}/>
    }    
});
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
Karlo A. López
  • 2,548
  • 3
  • 29
  • 56

2 Answers2

1

There are two problems in your code. The first is here:

<SingleChoiceGroup selectedItem={() => this.selectedDay()} numberOfButtons={7} prefix={"Text"}/>

Because your selectedItem arrow function doesn't take any arguments, the i value passed to it by SingleChoiceGroup is lost.

You could solve this by changing it to selectedItem={i => this.selectedDay(i)}, but rather than passing a function that calls this.selectedDay, you can just pass this.selectedDay itself, i.e.:

<SingleChoiceGroup selectedItem={this.selectedDay} numberOfButtons={7} prefix={"Text"}/>

The second problem, as Bartek pointed out in his answer, is that i in our for loop is a reference to the same object in each iteration of the loop, and since its final value is 7, that's the value that the event handler gets for each button. There's a more extensive discussion on that topic in this answer. The TL;DR is to use let instead of var to make the for loop initialize a new block-scoped variable each time.

Here are both of those changes in a working snippet:

const Button = props => <button type="button" {...props}/>;

var SingleChoiceGroup = React.createClass({
    render(){
        var numberOfButtons = this.props.numberOfButtons;
        var prefix = this.props.prefix;
        var buttons = [];

        for (let i = 0; i < numberOfButtons; i++){
            buttons.push(
                <Button key={i} value={i} onClick={() => this.props.selectedItem(i)}>{`${prefix} ${i+1}`}</Button>
            );
        }
        
        return <div>{buttons}</div>;
    }
});

var AnotherComponent = React.createClass({
    selectedDay(i){
      console.log('clicked %d', i);
    },

    render(){
        return <SingleChoiceGroup selectedItem={this.selectedDay} numberOfButtons={7} prefix="Text"/>
    }    
});

ReactDOM.render(<AnotherComponent/>, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Click on us: <div id="container"></div>
Community
  • 1
  • 1
Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • Sorry Im pretty new to react, what exactly does "bind something" means, I've tried `selectedItem={this.selectedDay}` but I always get the same value (7) in `selectedDay` method – Karlo A. López Feb 16 '17 at 16:54
  • I've fixed the issue you mentioned and updated my answer with a working snippet. "Bind" is not a term from React, it's a term from JavaScript. The reason you (may) need to use it is explained here: https://facebook.github.io/react/docs/handling-events.html (Scroll down to the paragraph beginning "You have to be careful...") – Jordan Running Feb 16 '17 at 17:06
  • Thanks, now I understand the difference between `var` and `let` – Karlo A. López Feb 16 '17 at 17:07
1

In your SingleChoiceGroup component use let insetad of var in for loop - otherwise click handlers will use last value of i which is 7 in your case (why? please see this SO answer for detailed explanation of closures in for loops):

 for(let i = 0;i<numberOfButtons;i++){
        buttons.push(
            <Button value={i} onClick={() => this.props.selectedItem(i)}>{prefix + " " + (i+1)}</Button>
        );
    }
Community
  • 1
  • 1
Bartek Fryzowicz
  • 6,464
  • 18
  • 27