I'm trying to dynamically add inputs when the user clicks the button to add a question.
Usually doing a controlled form is easy as your know what the field names are. But in this situation they are dynamic.
I've got a working solution but it mutates the state.
Is there a better way to do this?
Thanks
JSX
import React, { Component } from 'react';
import axios from 'axios';
import { saveAs } from 'file-saver';
class Form extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.handleForm = this.handleForm.bind(this);
this.addQuestion = this.addQuestion.bind(this);
this.removeQuestion = this.removeQuestion.bind(this);
this.state = {
questions: []
}
}
onChange(e, i) {
this.state.questions[i] = e.target.value;
this.setState({
questions: this.state.questions
})
}
handleForm(e) {
e.preventDefault();
const body = {
questions: this.state.questions
};
axios.post('/api/pdfs/create', body)
.then(() => axios.get('/api/pdfs/fetch', { responseType: 'blob' }))
.then((res) => {
const pdfBlob = new Blob([res.data], { type: 'application/pdf' });
return saveAs(pdfBlob, 'questions.pdf');
})
.catch(error => {
console.log(error.response)
});
}
addQuestion() {
this.setState({
questions: [...this.state.questions, '']
});
}
removeQuestion(index) {
this.setState({
questions: this.state.questions.filter((question, i) => i !== index)
});
}
render() {
return (
<div>
<button onClick={this.addQuestion}>Add Question</button>
<form onSubmit={this.handleForm}>
{this.state.questions.map((question, index) => (
<div key={index}>
<input type="text" name={`question-${question}`} onChange={(e) => this.onChange(e, index)} />
<button type="button" onClick={() => this.removeQuestion(index)}>x</button>
</div>
))}
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default Form;
this.state.questionsbefore you insert the new value. Then you won't mutate the state.