Categories
Development

Dynamic Forms in React, Cannot read property ‘map’ of undefined

Overview

So I’m only new to React and I’m having trouble trying to create a dynamic form. At the moment I want bug.pages and bug.steps_to_reproduce to be dynamic, meaning when I click a button like the “+ Add Page” it will add the new fields to bug.pages and display the new fields on the form.

The Problem

When I add more than one const to the render method, it will spit out an error (TypeError: Cannot read property 'map' of undefined) when I type in one of the pages or steps_to_reproduce fields (onChange). The error always points to the first const and if I remove one of the const’s it works as intended.

What I’ve Tried

So the “bugSteps” and “bugPages” const functions are near duplicates as is their corresponding if statements in the “onChange” function. If I remove all references to either “bugSteps” or “bugPages”, the form works as intended but can’t seem to find why it’s throwing an error with both.

Code

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createNewTicket } from '../../actions/ticket.action';

 class NewerTicketForm extends Component {

    constructor(props) {
        super(props);
        this.state = {
            name: '',
            type: 'Bug',
            status: 'Open',
            priority: 'Low',
            summary: '',
            bug: {
                pages: [
                    {
                        text: '',
                        url: ''
                    }
                ],
                page_sections: [],
                steps_to_reproduce: [
                    {
                        step: '',
                        order: 0
                    }
                ],
                expected_result: '',
                actual_result: '',
                attachments: []
            },
            improvement: {
                pages: [
                    {
                        text: '',
                        url: ''
                    }
                ],
                page_sections: [
                    {
                        text: '',
                        url: ''
                    }
                ],
                current_situation: '',
                improved_requirements: '',
                improved_requirements_attachments: [],
                attachments: []
            },
            new_feature: {
                requirements: [],
                requirements_attachments: [
                    {
                        type: '',
                        filename: '',
                        url: ''
                    }
                ]
            },
            comments: [],
            changes: [],
            submitted_by: '',
            assigned_to: '',
            project: '',
            expected_close_date: ''
        };

        this.onChange = this.onChange.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
        this.addPage = this.addPage.bind(this);
    }

    onChange(e) {
        console.log(e.target.name);
        if (['text', 'url'].some(str => e.target.name.includes(str))) {
            console.log('A');
            let field = e.target.name.split('-');
            console.log(field);
            let bugPages = [...this.state.bug.pages];           
            bugPages[parseInt(field[1])][field[0]] = e.target.value;
            console.log(bugPages);
            this.setState({ 
                bug: { 
                    pages: bugPages
                }
            });
        }  else if (['step', 'order'].some(str => e.target.name.includes(str))) {
            console.log('B');
            let field = e.target.name.split('-');
            console.log(field);
            let bugSteps = [...this.state.bug.steps_to_reproduce];           
            bugSteps[parseInt(field[1])][field[0]] = e.target.value;
            console.log(bugSteps);
            this.setState({ 
                bug: { 
                    steps_to_reproduce: bugSteps
                }
            });
        } else {
            console.log('C');
            this.setState({ [e.target.name]: e.target.value })
        }
    }

    onSubmit(e) {
        e.preventDefault();

        const ticketInfo = {
            name: this.state.name,
            type: this.state.type,
            status: this.state.status,
            priority: this.state.priority,
            summary: this.state.summary,
            submitted_by: this.props.user,
            project: this.props.project,
            expected_close_date: this.state.expected_close_date
        };

        this.props.createNewTicket(ticketInfo)
        .then(res => alert('success'))
        .catch(res => alert('failed'));

    }

    addPage(e) {
        let pages = this.state.bug.pages;

        pages.push({
            text: '',
            url: ''
        });

        this.setState({bug: {
            pages: pages
        }});
    }

    render() {
        const bugSteps = this.state.bug.steps_to_reproduce.map((val, idx) => {
            let stepId = `step-${idx}`;
            let key = `bug-steps-${idx}`;
            return (
                <div key={key}>
                    <div className="form-group row text-center">
                        <div className="col-2 ">
                            <label>Step {idx}: </label>
                        </div>
                        <div className="col-10">
                            <input type="text" className="form-control" name={stepId} value={val.step} onChange={this.onChange} />
                        </div>
                    </div>
                </div>
            );
        });

        const bugPages = this.state.bug.pages.map((val, idx) => {
            let textId = `text-${idx}`;
            let urlId = `url-${idx}`;
            let key = `bug-page-${idx}`;
            return (
                <div key={key}>
                    <div className="form-group row text-center">
                        <div className="col-2 ">
                            <label>Page Name: </label>
                        </div>
                        <div className="col-10">
                            <textarea className="form-control" name={textId} rows="3" value={val.text} onChange={this.onChange}></textarea>
                        </div>
                    </div>
                    <div className="form-group row text-center">
                        <div className="col-2 ">
                            <label>Page Url: </label>
                        </div>
                        <div className="col-10">
                            <input type="text" className="form-control" name={urlId} value={val.url} onChange={this.onChange} />
                        </div>
                    </div>
                </div>
            );
        });




        return (
            <div className="row">
                <div className="col-12 text-center">
                    <h2>Create New Ticket</h2>
                </div>
                <div className="col-12 ">
                    <form onSubmit={this.onSubmit}>
                        <div className="form-group row">
                            <div className="col-2 text-center">
                                <label>Ticket Name: </label>
                            </div>
                            <div className="col-10 text-center">
                                <input type="text" className="form-control" name="name" required value={this.state.name} onChange={this.onChange} />
                            </div>
                        </div>
                        <div className="form-group row text-center">
                            <div className="col-2 ">
                                <label>Type: </label>
                            </div>
                            <div className="col-10">
                                <select className="form-control" name="type" required onChange={this.onChange}>
                                    <option value="Bug" selected>Bug</option>
                                    <option value="Improvement">Improvement</option>
                                    <option value="New Feature">New Feature</option>
                                </select>
                            </div>
                        </div>
                        <div className="form-group row text-center">
                            <div className="col-2 ">
                                <label>Status: </label>
                            </div>
                            <div className="col-10">
                                <select className="form-control" name="status" required onChange={this.onChange}>
                                    <option value="Open" selected>Open</option>
                                    <option value="In Progress">In Progress</option>
                                    <option value="Awaiting SignOff">Awaiting SignOff</option>
                                </select>
                            </div>
                        </div>
                        <div className="form-group row text-center">
                            <div className="col-2 ">
                                <label>Priority: </label>
                            </div>
                            <div className="col-10">
                                <select className="form-control" name="status" required onChange={this.onChange}>
                                    <option value="Low" selected>Low</option>
                                    <option value="Medium">Medium</option>
                                    <option value="High">High</option>
                                    <option value="Critical">Critical</option>
                                </select>
                            </div>
                        </div>
                        <div className="form-group row text-center">
                            <div className="col-2 ">
                                <label>Summary: </label>
                            </div>
                            <div className="col-10">
                                <textarea className="form-control" name="summary" rows="3" value={this.state.summary} onChange={this.onChange}></textarea>
                            </div>
                        </div>
                        <div className="form-group row text-center">
                            <div className="col-2 ">
                                <label>Expected Close Date: </label>
                            </div>
                            <div className="col-10">
                                <input type="date" className="form-control" name="expected_close_date" value={this.state.expected_close_date} onChange={this.onChange} />
                            </div>
                        </div>


                        {
                            this.state.type === 'Bug' && 
                            <div>
                                <div className="row">
                                    <div className="col-12 text-center">
                                        <hr />
                                        <h3><small>Bug Details</small></h3>
                                    </div>
                                </div>
                                {bugPages}
                                <div className="row mb-4">
                                    <div className="col-12 text-center">
                                        <button type="button" className="btn btn-success" onClick={this.addPage}>+ Add Page</button>
                                    </div>
                                </div>                              
                                {bugSteps}
                                <div className="form-group row text-center">
                                    <div className="col-12 ">
                                        <label>Steps to Reproduce: </label>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Expected Results: </label>
                                    </div>
                                    <div className="col-10">
                                        <textarea className="form-control" name="text" rows="3" value={this.state.bug.expected_result} onChange={this.onChange}></textarea>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Actual Results: </label>
                                    </div>
                                    <div className="col-10">
                                        <textarea className="form-control" name="text" rows="3" value={this.state.bug.actual_result} onChange={this.onChange}></textarea>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Attachements: </label>
                                    </div>
                                    <div className="col-10">
                                        <input type="file" className="form-control-file" name="url" />
                                    </div>
                                </div>
                            </div>
                        }
                        {
                            this.state.type === 'Improvement' && 
                            <div>
                                <div className="row">
                                    <div className="col-12 text-center">
                                        <hr />
                                        <h3><small>Improvement Details</small></h3>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Page Name: </label>
                                    </div>
                                    <div className="col-10">
                                        <textarea className="form-control" name="text" rows="3" value={this.state.improvement.pages[0].text} onChange={this.onChange}></textarea>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Page Url: </label>
                                    </div>
                                    <div className="col-10">
                                        <input type="text" className="form-control" name="url" value={this.state.improvement.pages[0].url} onChange={this.onChange} />
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Page Section: </label>
                                    </div>
                                    <div className="col-10">
                                        <textarea className="form-control" name="text" rows="3" value={this.state.improvement.pages[0].text} onChange={this.onChange}></textarea>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Page Section Url: </label>
                                    </div>
                                    <div className="col-10">
                                        <input type="text" className="form-control" name="url" value={this.state.improvement.pages[0].url} onChange={this.onChange} />
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Current Situation: </label>
                                    </div>
                                    <div className="col-10">
                                        <textarea className="form-control" name="current_situation" rows="3" value={this.state.improvement.current_situation} onChange={this.onChange}></textarea>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Requirements: </label>
                                    </div>
                                    <div className="col-10">
                                        <textarea className="form-control" name="improved_requirements" rows="3" value={this.state.improvement.improved_requirements} onChange={this.onChange}></textarea>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Attachements: </label>
                                    </div>
                                    <div className="col-10">
                                        <input type="file" className="form-control-file" name="url" />
                                    </div>
                                </div>
                            </div>
                        }
                        {
                            this.state.type === 'New Feature' && 
                            <div>
                                <div className="row">
                                    <div className="col-12 text-center">
                                        <hr />
                                        <h3><small>New Feature Details</small></h3>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Requirements: </label>
                                    </div>
                                    <div className="col-10">
                                        <textarea className="form-control" name="requirements" rows="6" value={this.state.new_feature.requirements} onChange={this.onChange}></textarea>
                                    </div>
                                </div>
                                <div className="form-group row text-center">
                                    <div className="col-2 ">
                                        <label>Attachements: </label>
                                    </div>
                                    <div className="col-10">
                                        <input type="file" className="form-control-file" name="url" value={this.state.new_feature.requirements_attachments[0].url} onChange={this.onChange} />
                                    </div>
                                </div>
                            </div>
                        }
                        <div className="row">
                            <div className="col-12 text-center">
                                <button type="submit" className="btn btn-primary">Create Ticket</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        );
    }
}

NewerTicketForm.propTypes = {
    createNewTicket: PropTypes.func.isRequired
};

const mapStateToProps = state => ({
    company: state.company.currentCompany,
    project: state.project.currentProject,
    user: state.loggedInUser.user
});

export default connect(mapStateToProps, { createNewTicket })(NewerTicketForm);

Any help or 2nd pair of eyes would be greatly appreciated.

Leave a Reply

Your email address will not be published. Required fields are marked *