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

React页面PDF下载报错:Cannot read properties of undefined (reading 'length')

PDF下载报错:Cannot read properties of undefined (reading 'length')

问题场景

点击页面中的「Download PDF」按钮时,抛出TypeError错误:Cannot read properties of undefined (reading 'length'),无法生成并下载PDF文件。

错误原因

错误出在jsPDF-autotable的html配置项上:当前代码传入的是未挂载到DOM中的div元素,autoTable无法正确解析该元素内的表格结构,导致内部处理时尝试读取某个未定义对象的length属性,触发报错。

解决方案

提供两种可行修复方式:

方案1:直接用表头和数据生成表格(推荐)

跳过手动创建DOM元素的步骤,直接将表头和当前页数据传入autoTable,避免DOM解析问题:

  • 定义表头数组
  • 将当前页数据转换为表格行格式
  • 通过head和body参数传递给autoTable

方案2:临时挂载DOM元素

如果坚持使用html配置,需先将创建的表格容器临时挂载到页面,生成PDF后再移除:

  • 生成PDF前,把tableContainer添加到document.body
  • 完成PDF保存后,立即从DOM中移除该元素

修改后的代码(方案1实现)

import * as React from "react";
import { useState, useEffect } from "react";
import Button from "@mui/material/Button";
import axios from "axios";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import jsPDF from "jspdf";
import "jspdf-autotable";

const ButtonUsage = () => {
  const [data, setData] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [showTable, setShowTable] = useState(false);
  const recordsPerPage = 10;
  const pagesToShow = 5;

  useEffect(() => {
    if (showTable) {
      getProductData();
    }
  }, [showTable]);

  const getProductData = () => {
    axios.get("https://jsonplaceholder.typicode.com/todos").then((res) => {
      setData(res.data);
    });
  };

  const maxPages = data.length > 0 ? Math.ceil(data.length / recordsPerPage) : 1;

  const paginate = (pageNumber) => {
    setCurrentPage(pageNumber);
  };

  const downloadCurrentPageAsPDF = () => {
    if (data.length > 0) {
      generatePDF();
    } else {
      console.error("数据为空或未定义");
    }
  };

  const generatePDF = () => {
    const doc = new jsPDF();
    // 定义表头
    const headers = [["userId", "id", "title", "completed"]];
    // 处理当前页数据为表格行格式
    const currentRecords = data.slice(
      (currentPage - 1) * recordsPerPage,
      currentPage * recordsPerPage
    );
    const bodyData = currentRecords.map(row => [
      row.userId,
      row.id,
      row.title,
      row.completed ? "Yes" : "No"
    ]);

    // 生成PDF表格
    doc.autoTable({
      head: headers,
      body: bodyData,
      styles: {
        border: 1,
        padding: 8
      },
      headStyles: {
        fillColor: "#f0f0f0"
      }
    });

    doc.save("current_page.pdf");
  };

  return (
    <React.Fragment>
      <Button variant="contained" onClick={() => setShowTable(true)}>
        Get Data
      </Button>

      {showTable && data.length > 0 && (
        <div>
          <TableContainer component={Paper}>
            <Table
              sx={{ minWidth: 650 }}
              size="small"
              aria-label="a dense table"
            >
              <TableHead>
                <TableRow>
                  <TableCell>userId</TableCell>
                  <TableCell>id</TableCell>
                  <TableCell>title</TableCell>
                  <TableCell>completed</TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
                {data
                  .slice(
                    (currentPage - 1) * recordsPerPage,
                    currentPage * recordsPerPage
                  )
                  .map((row) => (
                    <TableRow
                      key={row.id}
                      sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
                    >
                      <TableCell component="th" scope="row">
                        {row.userId}
                      </TableCell>
                      <TableCell>{row.userId}</TableCell>
                      <TableCell>{row.id}</TableCell>
                      <TableCell>{row.title}</TableCell>
                      <TableCell>{row.completed ? "Yes" : "No"}</TableCell>
                    </TableRow>
                  ))}
              </TableBody>
            </Table>
          </TableContainer>
          <div className="pagination">
            <button onClick={() => paginate(1)} disabled={currentPage === 1}>
              First
            </button>
            <button
              onClick={() => paginate(currentPage - 1)}
              disabled={currentPage === 1}
            >
              Previous
            </button>
            {Array.from({ length: Math.min(pagesToShow, maxPages) }, (_, i) => (
              <button
                key={i + 1}
                onClick={() => paginate(i + 1)}
                className={currentPage === i + 1 ? "active" : ""}
              >
                {i + 1}
              </button>
            ))}
            <button
              onClick={() => paginate(currentPage + 1)}
              disabled={currentPage === maxPages}
            >
              Next
            </button>
            <button
              onClick={() => paginate(maxPages)}
              disabled={currentPage === maxPages}
            >
              Last
            </button>
            <button onClick={downloadCurrentPageAsPDF}>Download PDF</button>
          </div>
        </div>
      )}
    </React.Fragment>
  );
};

export default ButtonUsage;

方案2代码修改(仅修改generatePDF函数)

const generatePDF = () => {
  const tableContainer = document.createElement("div");
  tableContainer.style.width = "100%";
  tableContainer.style.overflow = "auto";

  const table = document.createElement("table");
  table.style.borderCollapse = "collapse";
  table.style.width = "100%";
  table.style.marginBottom = "20px";

  const tableHead = table.createTHead();
  const headRow = tableHead.insertRow(0);

  const headers = ["userId", "id", "title", "completed"];

  headers.forEach((headerText, index) => {
    const cell = headRow.insertCell(index);
    cell.textContent = headerText;
    cell.style.border = "1px solid black";
    cell.style.padding = "8px";
  });

  const tableBody = table.createTBody();

  const currentRecords = data.slice(
    (currentPage - 1) * recordsPerPage,
    currentPage * recordsPerPage
  );

  currentRecords.forEach((row) => {
    const newRow = tableBody.insertRow();
    const rowData = [
      row.userId,
      row.id,
      row.title,
      row.completed ? "Yes" : "No",
    ];
    rowData.forEach((cellData, index) => {
      const cell = newRow.insertCell(index);
      cell.textContent = cellData;
      cell.style.border = "1px solid black";
      cell.style.padding = "8px";
    });
  });

  tableContainer.appendChild(table);
  // 临时挂载到DOM
  document.body.appendChild(tableContainer);

  const doc = new jsPDF();
  doc.autoTable({
    html: tableContainer,
  });

  doc.save("current_page.pdf");
  // 移除临时元素
  document.body.removeChild(tableContainer);
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:24:55