Aakanksha Aakanksha - 2 months ago 31
React JSX Question

setState inside a loop - React

I am trying to build a questionnaire in react. For now, all my answers will be accepted using the textbox. These will then be sent to my api using the ajax call. I want my params to have following structure,

questionnaire: {
{question: "question", answer: "answer"},
{question: "question2", answer: "answer2"},
{question: "question3", answer: "answer3"}
}


My code so far is really messy but somehow i'm getting the desired results. However, i'm unable to update the state of my answer and keep getting the
Uncaught TypeError: Cannot read property 'setState' of undefined


import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom';
import SubmitButton from '../components/SubmitButton'

export default class QuestionaireContainer extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { q: this.props.data,
questionaire: [],
};
}
componentDidMount() {
var q = this.state.q
var questionaire = this.state.questionaire
q.map(function(map){
questionaire.push({"id": map.id,
"question": map.question,
"answer": '',
});
})
this.setState({questionaire: questionaire})
}

submit(e){
e.preventDefault();
var data = {
questionaires: this.state.questionaire
}
$.ajax({
type: "GET",
url: "/questionaries/save_questions",
data: data,
dataType: 'JSON',
success: ()=>{
alert("hello")
}
})
}


render() {
var questionNodes = this.state.questionaire.map(function(question){

var updateTextBox = (e) => {
console.log(question.answer);
this.setState({"answer": e.target.value});
}

return (
<li key={question.id}>
<div className="box-sed">
<div className="que-set registration question-type">
<span className="icon-ahead">
<i aria-hidden="true" className="fa fa-question-circle"></i>
</span>
<h2>{question.question}</h2>
<p>Lorem Ipsum has been the industry's standard dummy text ever since the 1500's, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</p>
</div>
<div className="registration question-type">
<input type="text" placeholder="Type answer" className="input-text" value={question.answer} onChange={updateTextBox} />
</div>
</div>
</li>
);
});
return (
<div className="question">
<form onSubmit={this.submit.bind(this)}>
<ul>
{questionNodes}
</ul>
<SubmitButton/>
</form>
</div>
);
}
}


Also, the params are a array and not a hash now.I cant figure out how else to set the initial state with my questions. The code might be really dirty but i'have only been able to do this yet.

I might not be making myself very clear so kindly bear with me and drop a comment so i could help you to better understand what i want. Any changes and suggestions will be great. Thanks in advance :)

--UPDATE--

I ended up taking Daniel's advice and put the text box in another react components and called it in the parent one.

Also, i omitted the whole state thing inside loop and moved forward with the
this.props
. I updated the
text box's
state in the child component (
QuestionaireComponent
) and used its state in the parent
QuestionaireContainer
component using the
refs
.

Following is my final code that got me working:

QuestionaireContainer.jsx

import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom';
import SubmitButton from '../components/SubmitButton'
import QuestionaireComponent from '../components/QuestionaireComponent'

export default class QuestionaireContainer extends React.Component {

submit(e){
e.preventDefault();
var data = {
questionaires: []
}
this.props.data.map(map => {
data.questionaires.push(this.refs[map.id].state)
});
console.log(data);
console.log("Done");
}
render() {
var questionNodes = this.props.data.map(questions => {
return (
<QuestionaireComponent question={questions.question} key={questions.id} id = {questions.id} ref={questions.id}/>
);
});
return (
<div className="question">
<form onSubmit={this.submit.bind(this)}>
<ul>
{questionNodes}
</ul>
<SubmitButton/>
</form>
</div>
);
}
}


and QuestionaireComponent.jsx

import React, {PropTypes} from 'react'
export default class QuestionaireComponent extends React.Component {
static propTypes = {
question: PropTypes.string.isRequired,
};
constructor(props) {
super(props);
this.state = { answer: '',
question: this.props.question,
id: this.props.id,
};
}
updateTextBox = (e) => {
this.setState({answer: e.target.value})
}
render() {
return (
<li>
<div className="box-sed">
<div className="que-set registration question-type">
<span className="icon-ahead">
<i aria-hidden="true" className="fa fa-question-circle"></i>
</span>
<h2>{this.state.question}</h2>
<p>Lorem Ipsum has been the industry's standard dummy text ever since the 1500's, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</p>
</div>
<div className="registration question-type">
<input type="text" placeholder="Type answer" className="input-text" value={this.state.answer} onChange={this.updateTextBox} />
</div>
</div>
</li>
);
}
}


Hope this helps anyone whose looking for something similar.

Answer

Change this:

var questionNodes = this.state.questionaire.map(function(question){

Into

var questionNodes = this.state.questionaire.map(question => {

Because in your code function(question) override the context variable this.

Comments