React类组件提交表格数据报错:props.customerTable.map不是函数
问题描述
在React类组件中尝试将customerTable数据提交到Customer数组时,出现错误:Uncaught TypeError: props.customerTable.map is not a function。相关代码如下:
CustomerBill.js
import React, { Component } from "react"; import "bootstrap/dist/css/bootstrap.min.css"; import axios from "axios"; import $ from "jquery"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import CustomerTable from "./table"; class CustomerBill extends Component { constructor() { super(); this.state = { Customer: "", customerTable: { index: Math.random(), id: "", paymentDescription: "", quantity: "", unitPrice: "", }, }; this.handleSubmit = this.handleSubmit.bind(this); this.handleChange = this.handleChange.bind(this); } handleChange = (e) => { if ( ["id", "paymentDescription", "quantity", "unitPrice"].includes( e.target.name ) ) { let customerTable = { ...this.state.customerTable }; customerTable[e.target.dataset.id][e.target.name] = e.target.value; } else { this.setState({ [e.target.name]: e.target.value }); } }; addNewRow = () => { this.setState((prevState) => ({ customerTable: [ ...prevState.taskList, { index: Math.random(), id: "", paymentDescription: "", quantity: "", unitPrice: "", }, ], })); }; clickOnDelete(record) { this.setState({ customerTable: this.state.customerTable.filter((r) => r !== record), }); } handleSubmit(event) { event.preventDefault(); const bill = { Customer: this.state.customerTable, }; axios .post(`http://localhost:4000/customer/bill`, bill) .then((response) => { console.log(response.data); return $("alert-success").show(); }); this.setState({ Customer: "", }); } render() { let { customerTable } = this.state; return ( <div> <div className="container"> <div className="form-div"> <h1>Customer Bill</h1> <Box component="form" onSubmit={this.handleSubmit} noValidate sx={{ mt: 1 }} onChange={this.handleChange} > <div className="row" style={{ marginTop: 20 }}> <div className="col-sm-3"></div> <div className="col-sm-12"> <div className="card"> <div className="card-header text-center"> Bill Description </div> <div className="card-body"> <div className="row"> <div className="col-sm-4"></div> </div> <table> <thead> <tr> <th className="text-center"> sN </th> <th className="text-center"> Id </th> <th className="text-center"> Payment Description </th> <th className="text-center"> Quantity </th> </tr> </thead> <tbody> <CustomerTable id="customerTable" add={this.addNewRow.bind(this)} delete={this.clickOnDelete.bind(this)} customerTable={customerTable} /> </tbody> </table> </div> </div> </div> <div className="col-sm-1"></div> </div> <Button type="submit" fullWidth sx={{ mt: 3, mb: 2 }}> <span className="btn btn-warning btn-block form-control form-group"> Submit </span> </Button> </Box> </div> </div> </div> ); } } export default CustomerBill;
table.js
import React from "react"; const CustomerTable = (props, val, idx, post) => { return props.customerTable.map((val, idx, post) => { let id = `id-${idx}`, paymentDescription = `paymentDescription-${idx}`, quantity = `quantity-${idx}`, unitPrice = `unitPrice-${idx}`; return ( <tr key={val.index}> <td> <input type="text" name="id" data-id={idx} id={id} className="form-control " /> </td> <td> <input type="text" name="paymentDescription" id={paymentDescription} data-id={idx} className="form-control " /> </td> <td> <input type="text" name="quantity" id={quantity} data-id={idx} className="form-control" /> </td> <td> <input type="text" name="unitPrice" id={unitPrice} data-id={idx} className="form-control" /> </td> <td> {idx === 0 ? ( <button onClick={() => props.add()} type="button" className="btn btn-primary" > Add Row </button> ) : ( <button className="btn btn-danger" onClick={() => props.delete(val)} > Delete Row </button> )} </td> </tr> ); }); }; export default CustomerTable;
错误原因与修复方案
核心错误原因
- 初始状态类型不匹配:
customerTable初始值是单个对象,但子组件中调用了数组的map方法,对象没有该方法,直接导致报错。 - 状态更新逻辑错误:
addNewRow方法中错误引用了不存在的prevState.taskList,应该用prevState.customerTable。handleChange方法中错误地以对象方式修改数组项,需要正确遍历数组更新对应索引的元素。
- 输入框未绑定value:无法同步状态和输入框内容,导致数据无法正确收集。
- 提交后状态未重置:提交后未清空
customerTable,影响后续操作。
修改后的代码
CustomerBill.js
import React, { Component } from "react"; import "bootstrap/dist/css/bootstrap.min.css"; import axios from "axios"; import $ from "jquery"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import CustomerTable from "./table"; class CustomerBill extends Component { constructor() { super(); this.state = { // 初始化为数组,包含一行默认数据 customerTable: [ { index: Math.random(), id: "", paymentDescription: "", quantity: "", unitPrice: "", }, ], }; this.handleSubmit = this.handleSubmit.bind(this); this.handleChange = this.handleChange.bind(this); this.addNewRow = this.addNewRow.bind(this); this.clickOnDelete = this.clickOnDelete.bind(this); } // 处理输入框变化,更新对应行的状态 handleChange = (e) => { const { name, value, dataset } = e.target; const rowIndex = parseInt(dataset.id); if (["id", "paymentDescription", "quantity", "unitPrice"].includes(name)) { this.setState(prevState => ({ customerTable: prevState.customerTable.map((row, idx) => { if (idx === rowIndex) { return { ...row, [name]: value }; } return row; }) })); } }; // 添加新行 addNewRow() { this.setState(prevState => ({ customerTable: [ ...prevState.customerTable, { index: Math.random(), id: "", paymentDescription: "", quantity: "", unitPrice: "", }, ], })); } // 删除指定行 clickOnDelete(record) { this.setState(prevState => ({ customerTable: prevState.customerTable.filter(row => row.index !== record.index), })); } // 提交表单 handleSubmit(event) { event.preventDefault(); const bill = { Customer: this.state.customerTable, }; axios .post(`http://localhost:4000/customer/bill`, bill) .then((response) => { console.log(response.data); $(".alert-success").show(); // 提交后重置表格数据 this.setState({ customerTable: [ { index: Math.random(), id: "", paymentDescription: "", quantity: "", unitPrice: "", }, ], }); }) .catch(err => { console.error("提交失败", err); }); } render() { const { customerTable } = this.state; return ( <div> <div className="container"> <div className="form-div"> <h1>Customer Bill</h1> <Box component="form" onSubmit={this.handleSubmit} noValidate sx={{ mt: 1 }} > <div className="row" style={{ marginTop: 20 }}> <div className="col-sm-3"></div> <div className="col-sm-12"> <div className="card"> <div className="card-header text-center"> Bill Description </div> <div className="card-body"> <table className="table table-bordered"> <thead> <tr> <th className="text-center">SN</th> <th className="text-center">Id</th> <th className="text-center">Payment Description</th> <th className="text-center">Quantity</th> <th className="text-center">Unit Price</th> <th className="text-center">Action</th> </tr> </thead> <tbody> <CustomerTable add={this.addNewRow} delete={this.clickOnDelete} customerTable={customerTable} handleChange={this.handleChange} /> </tbody> </table> </div> </div> </div> <div className="col-sm-1"></div> </div> <Button type="submit" fullWidth sx={{ mt: 3, mb: 2 }}> <span className="btn btn-warning btn-block form-control form-group"> Submit </span> </Button> </Box> </div> </div> </div> ); } } export default CustomerBill;
table.js
import React from "react"; const CustomerTable = (props) => { const { customerTable, add, delete: deleteRow, handleChange } = props; // 处理空数组情况,避免报错 if (!customerTable.length) { return <tr><td colSpan="6" className="text-center">No rows to display</td></tr>; } return customerTable.map((val, idx) => { const id = `id-${idx}`, paymentDescription = `paymentDescription-${idx}`, quantity = `quantity-${idx}`, unitPrice = `unitPrice-${idx}`; return ( <tr key={val.index}> <td className="text-center">{idx + 1}</td> <td> <input type="text" name="id" data-id={idx} id={id} className="form-control" value={val.id} onChange={handleChange} /> </td> <td> <input type="text" name="paymentDescription" id={paymentDescription} data-id={idx} className="form-control" value={val.paymentDescription} onChange={handleChange} /> </td> <td> <input type="text" name="quantity" id={quantity} data-id={idx} className="form-control" value={val.quantity} onChange={handleChange} /> </td> <td> <input type="text" name="unitPrice" id={unitPrice} data-id={idx} className="form-control" value={val.unitPrice} onChange={handleChange} /> </td> <td className="text-center"> {idx === 0 ? ( <button onClick={add} type="button" className="btn btn-primary" > Add Row </button> ) : ( <button className="btn btn-danger" onClick={() => deleteRow(val)} > Delete Row </button> )} </td> </tr> ); }); }; export default CustomerTable;
关键修改点说明
- 初始化
customerTable为数组:确保子组件可以正常调用map方法。 - 修正
addNewRow方法:使用prevState.customerTable来扩展数组,添加新行。 - 优化
handleChange逻辑:通过map遍历数组,更新指定索引的行数据,确保状态正确同步。 - 输入框绑定
value和onChange:实现受控组件,保证输入内容与状态一致。 - 删除逻辑优化:通过
index字段区分不同行,避免引用比较的问题。 - 提交后重置状态:清空表格数据,方便下一次输入。
- 增加空数组判断:避免数组为空时
map方法报错。
内容的提问来源于stack exchange,提问作者user20183888
相关产品推荐
相关产品推荐

