首页 > 解决方案 > React 中的动态表单,无法读取未定义的属性“映射”

问题描述

概述

所以我只是 React 的新手,我在尝试创建动态表单时遇到了麻烦。目前我希望 bug.pages 和 bug.steps_to_reproduce 是动态的,这意味着当我单击“+ 添加页面”之类的按钮时,它会将新字段添加到 bug.pages 并在表单上显示新字段。

问题

当我向渲染方法添加多个 const时,TypeError: Cannot read property 'map' of undefined当我输入其中一个页面或 steps_to_reproduce 字段 (onChange) 时,它会吐出一个错误 ()。错误总是指向第一个 const,如果我删除其中一个 const,它会按预期工作。

我试过的

因此,“bugSteps”和“bugPages”const 函数与“onChange”函数中的相应 if 语句一样,几乎是重复的。如果我删除对“bugSteps”或“bugPages”的所有引用,表单会按预期工作,但似乎无法找到为什么它会同时抛出错误。

代码

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);

任何帮助或第二双眼睛将不胜感激。

标签: javascriptreactjsreduxjsx

解决方案


当您更新“错误”状态时,您可能会丢失以前设置的信息。你可以试试:

this.setState((prevState) => { 
    bug: {
        ...prevState.bug,
        pages: bugPages
    }
});

this.setState((prevState) => { 
    bug: {
        ...prevState.bug,
        steps_to_reproduce: bugSteps
    }
});

如果您在使用对象之前对其进行测试,这也将有助于防止错误,例如

let bugSteps;
if (this.state.bug && this.state.bug.steps_to_reproduce && this.state.bug.steps_to_reproduce.length) {
    bugSteps = this.state.bug.steps_to_reproduce.map((val, idx) => {
        ...
    }
}

推荐阅读