staxwell staxwell - 1 month ago 7
React JSX Question

How do I change data on a specific instance of a React component?

How do I update data on a specific instance of a component? Right now my

addOption
function is pushing elements to every instance's
options
array when really I just want it to update the particular instance I'm clicking on. As a result, it's rendering
option
components for every
question
component when I run the
addOption
method. Thanks!

// Quiz Builder Form

import React from 'react';
import Question from './Question';
import firebase from 'firebase';

const QuizBuilderForm = React.createClass({
getInitialState: function() {
return {
questions: []
};
},
addQuestion: function(id) {
var questions = this.state.questions;
questions.push({ qtext : "", options: [""], id: this.state.questions.length, answer: "" });
this.setState({
questions: questions
});
},
addOption: function(index) {
var questions = this.state.questions;
for (index = 0; index < questions.length; index++) {
questions[index].options.push("");
}
this.setState({
questions: questions
})
},
handleSubmit: function(event) {
event.preventDefault();
console.log(this.state.questions);
this.firebaseRef = firebase.database().ref('quizzes');
this.firebaseRef.push({
question: this.state.questions
});
this.refs.form.reset();
this.setState({
question: [{ qtext : "", options:[""], id: 0, answer: ""}]
});
},
handleChange: function(event) {
// Do something
},
componentDidMount: function() {
this.addQuestion();
},
render: function() {
var questions = this.state.questions.map((question, index) => <Question key={index} index={index} ref={'question: ' + index} question={question} addOption={this.addOption} handleChange={this.handleChange} {...this.props}/>);
return (
<form className="quiz-form" onSubmit={this.handleSubmit} ref="form">
{questions}
<button type="button" className="add-question" onClick={this.addQuestion} disabled={this.state.questions.length === 5}>{this.state.questions.length < 5 ? 'Add another question' : 'Question limit reached!'}</button>
<button type="submit">Create Quiz</button>
</form>
);
}
});

export default QuizBuilderForm;

// Question component

import React from 'react';
import Option from './Option';

const Question = React.createClass({
render: function() {
var options = this.props.question['options'].map((option, index) => <Option key={index} index={index} option={option}/>);
return (
<div className="question">
<input type="text" value="" onChange={this.props.handleChange}></input>
{options}
<button type="button" onClick={this.props.addOption}>Add another option</button>
</div>
);
}
});

export default Question;

// Option component

import React from 'react';

const Option = React.createClass({
render: function() {
return (
<input type="text" value="" placeholder="Enter an answer here"></input>
);
}
});

export default Option;

Answer

Well, in your addOption(index) method, you are not using index to update just the question you want, because at the for loop, it is iterating through all questions and adding an option to them.

I think that something like this would work:

  addOption: function(index) {
    var questions = this.state.questions,
        question = questions[index];

    question = Object.assign({}, question, {options: question.options.concat('')});

    questions = questions
                  .slice(0, index)
                  .concat([question])
                  .concat(questions.slice(index + 1));

    this.setState({
        questions: questions
    })
},

And change this in your <Question /> component:

const Question = React.createClass({
  addOption: function () {
    this.props.addOption(this.props.index);
  },
  render: function() {
    var options = this.props.question['options'].map((option, index) => <Option key={index} index={index} option={option}/>);
    return (
        <div className="question">
            <input type="text" value="" onChange={this.props.handleChange}></input>
            {options}
            <button type="button" onClick={this.addOption}>Add another option</button>
        </div>
    );
  }
});

At your addOptions() in the <QuizBuilderForm /> component, you're receiving an event parameter, but you want to receive the index of the question clicked. So, in order to do so, you could create a method in <Question /> to handle the button event and call the this.props.addOption() with the correct parameter.

Try to avoid functions that would mutate state, so that's why I am using Object.assign(), Array.slice() and Array.concat() (things are a little bit easier with ES6+ spread operator over arrays and objects, so you could write question = {...question, { options: [...question.options, '']).

Comments