1

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;
1
  • Make a copy of this.state.questions before you insert the new value. Then you won't mutate the state. Commented May 14, 2019 at 21:25

1 Answer 1

2

You are mutating the state only in your onChange call, and that can be fixed easily:

 onChange(e, i) {        
    this.setState({
        questions: this.state.questions.map((v, i2) => i === i2 ? e.target.value : v),
    });
}

(This won't change the functionality though, its just a "best practice improvement")

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.