wongtam
wongtam

Reputation: 13

Handling onChange and setting state for array of input

just a bit of background, I am really new to javascript and web development but have been having fun doing some tutorials on React. Also my first time posting on stackoverflow!

I am building a component to show a list of yes/no questions and users have to respond by selecting radio buttons and optionally adding some comments in a textarea. I'm not really sure how I am supposed to set the state for an array of inputs generated using map.

I have an array holding my questions:

var questions = [
    {id:"1", text:"Is the source up to date?"},
    {id:"2", text:"Question 2 placeholder"},
    {id:"3", text:"Question 3 placeholder"},
]

and here is my (unfinished) component:

var QuestionList = React.createClass({
    getInitialState: function () {
        return {
            commentText: "",
        }
    },

    onUpdateComments: function (e) {
        this.setState({
            commentText: e.target.value
        });
    },
    render: function () {
        var QuestionLines = this.props.questions.map(function(question) {
            return (
                <div key={question.id}>
                    <div>
                        <div>
                            {question.text}
                        </div>
                        <label>
                            <input type="radio" name={question.id} value = {question.id+'Y'}/>Yes
                        </label>
                        <label>
                            <input type="radio" name={question.id} value = {question.id+'N'}/>No
                        </label>
                    </div>
                    <div>
                        <textarea 
                            name = {question.id}
                            onChange = {this.onUpdateComments}
                            placeholder="Enter comments here" 
                            value={this.state.commentText} />
                    </div>
                </div>
            );
        }, this);
        return (
            <div>
                {QuestionLines}
            </div>
        )
    }
});

The app right now displays the same text in all 3 textareas, and I can see that this is because I am storing all changes to textarea in the same commmentText state. However, I am really stumped as to what I need to do to separate these and make this work. Any help would be appreciate.

Also, as I mentioned I am super new to this so if anything is off about how I am structuring my component, please let me know.

Thanks!

Upvotes: 1

Views: 8087

Answers (3)

luiscrjr
luiscrjr

Reputation: 7278

I would do something like that:

var QuestionList = React.createClass({
    getInitialState: function () {
        return { comments: {} } //set internal state comment as an empty object
    },

    onUpdateComments: function (id, e) {

        /*
            you can modify your state only using setState. But be carefull when trying to grab actual state and modify it's reference.
            So, the best way is to create a new object (immutable pattern), and one way of doing that is to use Object.assign
        */
        var comments = Object.assign({}, this.state.comments);

        /* set, for instance, comment[1] to "some text" */
        comments[id] = e.target.value;

        /* set the state to the new variable */
        this.setState({comments: comments});


    },
    render: function () {
        var QuestionLines = this.props.questions.map(function(question) {

            /* grab the comment for this ID. If undefined, set to empty */
            var comment = this.state.comments[question.id] || "";

            return (
                <div key={question.id}>
                    <div>
                        <div>
                            {question.text}
                        </div>
                        <label>
                            <input type="radio" name={question.id} value = {question.id+'Y'}/>Yes
                        </label>
                        <label>
                            <input type="radio" name={question.id} value = {question.id+'N'}/>No
                        </label>
                    </div>
                    <div>
                        <textarea 
                            name = {question.id}
                            onChange = {this.onUpdateComments.bind(this,question.id)}
                            placeholder="Enter comments here" 
                            value={comment} />
                    </div>
                </div>
            );
        }, this);
        return (
            <div>
                {QuestionLines}
            </div>
        )
    }
});

Upvotes: 1

L_K
L_K

Reputation: 2986

Just set commentText as an object:

var QuestionList = React.createClass({
    getInitialState: function () {
        return {
            commentText: {},
        }
    },

    onUpdateComments: function (e) {

        // Note that e.target.name is question.id
        var target = e.target;
        this.state.commentText[target.name] = target.value;
        this.forceUpdate();
    },
    render: function () {
        var QuestionLines = this.props.questions.map(function(question) {

            var id = question.id; // store id

            return (
                <div key={id}>
                    <div>
                        <div>
                            {question.text}
                        </div>
                        <label>
                            <input type="radio" name={id} value = {id+'Y'}/>Yes
                        </label>
                        <label>
                            <input type="radio" name={id} value = {id+'N'}/>No
                        </label>
                    </div>
                    <div>
                        <textarea 
                            name = {id}
                            onChange = {this.onUpdateComments}
                            placeholder="Enter comments here" 
                            value={this.state.commentText[id]} />
                    </div>
                </div>
            );
        }, this);
        return (
            <div>
                {QuestionLines}
            </div>
        )
    }
});

See difference between onUpdateComments and value={this.state.commentText[id]}.

Note: If you use babel to compile your code, you can write onUpdateComments like that:

onUpdateComments: function (e) {

    // Note that e.target.name is question.id
    var target = e.target;
    this.setState(function(previousState) {
      return {
        commentText: {
          ...previousState.commentText, 
          [target.name]: target.value
        }
      }
    });
},

Upvotes: 0

ianks
ianks

Reputation: 1768

One solution would be to have a commentTextStore object, where the keys(properties) are the commentIds and then you have each question text area write to the value corresponding the commentId.

Upvotes: 0

Related Questions