javascript - 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);
任何帮助或第二双眼睛将不胜感激。
解决方案
当您更新“错误”状态时,您可能会丢失以前设置的信息。你可以试试:
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) => {
...
}
}
推荐阅读
- python - Discord 邀请链接 -> 服务器 ID
- azure - 经典模式下的条件变量
- python - 在 Python 中将 EXE 文件编码/解码为 base64
- javascript - rete.js 示例页面不起作用我该怎么办?
- python - python设置字典值的更好方法
- .htaccess - 如何将所有非 www 流量重定向到 digitalocean 中的 www
- ios - 如何在 iOS 中强制屏幕旋转为纵向但不倒置?
- python - 将 Excel 导入 Google Colab:“TypeError:预期的 str、字节或 os.PathLike 对象,而不是 NoneType”
- php - 当页面仍在工作时,如何刷新()或 ob_flush()响应标头?
- ssl - WHM - 自动将通配符 SSL 安装到所有子域