You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

错误原因与修复方案

核心错误原因

  1. 初始状态类型不匹配:customerTable初始值是单个对象,但子组件中调用了数组的map方法,对象没有该方法,直接导致报错。
  2. 状态更新逻辑错误:
    • addNewRow方法中错误引用了不存在的prevState.taskList,应该用prevState.customerTable。
    • handleChange方法中错误地以对象方式修改数组项,需要正确遍历数组更新对应索引的元素。
  3. 输入框未绑定value:无法同步状态和输入框内容,导致数据无法正确收集。
  4. 提交后状态未重置:提交后未清空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;

关键修改点说明

  1. 初始化customerTable为数组:确保子组件可以正常调用map方法。
  2. 修正addNewRow方法:使用prevState.customerTable来扩展数组,添加新行。
  3. 优化handleChange逻辑:通过map遍历数组,更新指定索引的行数据,确保状态正确同步。
  4. 输入框绑定value和onChange:实现受控组件,保证输入内容与状态一致。
  5. 删除逻辑优化:通过index字段区分不同行,避免引用比较的问题。
  6. 提交后重置状态:清空表格数据,方便下一次输入。
  7. 增加空数组判断:避免数组为空时map方法报错。

内容的提问来源于stack exchange,提问作者user20183888

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:20:30