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

React.js页面崩溃:读取undefined属性(invoice_num)求助

发票详情页面崩溃问题排查与解决

我使用Redux Toolkit和React Router v6开发了带有「View」按钮的发票列表,点击按钮可打开发票详情页面,同时具备新增发票功能。但新增发票后点击「View」按钮页面会崩溃,控制台报错:

Uncaught TypeError: Cannot read properties of undefined (reading 'invoice_num')

点击已有发票后刷新页面也会出现同样问题,报错指向InvoiceItem.js文件。


相关代码

InvoiceItem.js

import React from "react";

import { useParams } from "react-router-dom";

import InvoiceItemDescription from "../Invoice/InvoiceItemDescription";

import { INVOICES_LIST } from "./InvoicesList";

const InvoiceItem = () => {
  const params = useParams();

  const invoice = INVOICES_LIST.find(
    (invoice) => invoice.id === params.invoiceId
  );

  return (
    <InvoiceItemDescription
      invoiceNumber={invoice.invoice_num}
      status={invoice.status}
      order_date={invoice.order_date}
      bill_from={invoice.bill_from}
      bill_from_address={invoice.bill_from_address}
      bill_from_email={invoice.bill_from_email}
      bill_from_fax={invoice.bill_from_fax}
      bill_from_phone={invoice.bill_from_phone}
      bill_to={invoice.bill_to}
      bill_to_address={invoice.bill_to_address}
      bill_to_email={invoice.bill_to_email}
      bill_to_fax={invoice.bill_to_fax}
      bill_to_phone={invoice.bill_to_phone}
      item_name={invoice.ITEMS.item_name}
      unit_costs={invoice.ITEMS.unit_costs}
      unit={invoice.ITEMS.unit}
      price={invoice.ITEMS.price}
    />
  );
};

export default InvoiceItem;

InvoiceItemDescription.js

import React from "react";

import Wrapper from "../../UI/Wrapper";
import Footer from "../../UI/Footer";

import classes from "./InvoiceItemDescription.module.css";

import { Link } from "react-router-dom";

const InvoiceItemDescription = (props) => {
  let counter = 1;

  return (
    <Wrapper isShrinked={props.isShrinked}>
      <div className={classes.wrapper}>
        <div className={classes["content-wrapper"]}>
          <div className={classes["main-wrapper"]}>
            <div className={classes["upper-buttons"]}>
              <div className={classes["upper-buttons-wrapper"]}>
                <Link to="/invoices">
                  <button type="button" className={classes["go-to-invoices"]}>
                    Go To Invoices
                  </button>
                </Link>
                <Link to="/invoices/edit-invoice">
                  <button type="button" className={classes["edit-invoice"]}>
                    Edit Invoice
                  </button>
                </Link>
              </div>
            </div>
            <div className={classes.content}>
              <div className={classes["invoice-info"]}>
                <div className={classes.info}>
                  <h3>Invoice Info</h3>
                  <span>{props.invoiceNumber}</span>
                </div>
                <div className={classes.order}>
                  <p>
                    <span className={classes["order-status"]}>
                      Order Status:
                    </span>
                    <span className={classes.status}>{props.status}</span>
                  </p>
                  <p>
                    <span className={classes["order-date"]}>Order Date:</span>
                    <span className={classes.date}>{props.order_date}</span>
                  </p>
                </div>
              </div>
              <div className={classes.bills}>
                <div className={classes["bill-from"]}>
                  <h3>Bill From</h3>
                  <div>
                    <p className={classes["bill-from-info"]}>
                      <span className={classes.name}>{props.bill_from}</span>
                      <span className={classes.email}>
                        {props.bill_from_email}
                        <br></br>
                        <br></br> {props.bill_from_address}
                        <br></br>
                        <br></br>
                        <br></br> {props.bill_from_phone}
                      </span>
                    </p>
                  </div>
                </div>
                <div className={classes["bill-to"]}>
                  <h3>Bill To</h3>
                  <p className={classes["bill-to-info"]}>
                    <span className={classes.name}>{props.bill_to}</span>
                    <span className={classes.email}>
                      {props.bill_to_email} <br></br>
                      <br></br> {props.bill_to_address} <br></br>
                      <br></br>
                      <br></br>
                      {props.bill_to_fax} <br></br> {props.bill_to_phone}
                    </span>
                  </p>
                </div>
              </div>
              <div className={classes.table}>
                <table>
                  <colgroup>
                    <col className={classes.col1}></col>
                    <col className={classes.col2}></col>
                    <col className={classes.col3}></col>
                    <col className={classes.col4}></col>
                    <col className={classes.col5}></col>
                  </colgroup>
                  <thead>
                    <tr>
                      <td>#</td>
                      <td>Item Name</td>
                      <td>Unit Costs</td>
                      <td>Unit</td>
                      <td>Price</td>
                    </tr>
                  </thead>
                  <tbody>
                    <tr>
                      <td>{counter++}</td>
                      <td>{props.item_name}</td>
                      <td>{props.unit_costs}</td>
                      <td>{props.unit}</td>
                      <td>{props.price}</td>
                    </tr>
                  </tbody>
                </table>
              </div>
              <div className={classes.total}>
                <p>
                  Sub-total:
                  <span>$13300</span>
                </p>
                <p>
                  Vat:
                  <span>$13300</span>
                </p>
                <h3>
                  Grand Total:
                  <span>$14630</span>
                </h3>
              </div>
            </div>
            <div className={classes["lower-btn"]}>
              <button type="button">Send Invoice</button>
            </div>
          </div>
        </div>
      </div>
      <Footer />
    </Wrapper>
  );
};

export default InvoiceItemDescription;

Invoice.js

import React from "react";

import classes from "./Invoice.module.css";

import { useDispatch } from "react-redux";
import { Link } from "react-router-dom";

import { invoiceActions } from "../../store/invoice-slice";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

import { faTrash } from "@fortawesome/free-solid-svg-icons";

const Invoice = (props) => {
  const { id, invoice_num, bill_from, bill_to, status } = props.invoiceItem;

  const dispatch = useDispatch();

  const removeInvoiceItem = () => {
    dispatch(invoiceActions.removeInvoice(id));
  };

  return (
    <tr className={classes.height}>
      <td>
        <span className={classes.checkbox}>
          <input type="checkbox"></input>
        </span>
      </td>
      <td>
        <span>{invoice_num}</span>
      </td>
      <td>
        <span>{bill_from}</span>
      </td>
      <td>
        <span>{bill_to}</span>
      </td>
      <td>
        <span>14300</span>
        {/* This should be a dynamic value later */}
      </td>
      <td>
        <span
          className={`${
            status === "Pending" ? classes["status-pending"] : ""
          } ${status === "Delivered" ? classes["status-delivered"] : ""} ${
            status === "Shipped" ? classes["status-shipped"] : ""
          }`}
        >
          {status}
        </span>
      </td>
      <td>
        <div className={classes.buttons}>
          <Link to={`/invoices/invoice-description/${id}`}>
            <button className={classes["view-btn"]}>View</button>
          </Link>
          <button className={classes["delete-btn"]} onClick={removeInvoiceItem}>
            <FontAwesomeIcon icon={faTrash} />
          </button>
        </div>
      </td>
    </tr>
  );
};

export default Invoice;

问题原因

  1. 数据源不统一:InvoiceItem.js里从静态的INVOICES_LIST查找发票,但新增的发票只存在Redux store中,静态列表没有新增条目,find方法返回undefined,后续访问invoice.invoice_num就会触发报错。
  2. 刷新状态丢失:刷新页面后Redux状态重置,原有发票如果只存在store里(静态列表没有),同样会找不到对应条目导致报错。

解决方案

1. 从Redux Store获取发票数据

修改InvoiceItem.js,改用useSelector从Redux store中获取所有发票,而不是依赖静态列表:

import React from "react";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import InvoiceItemDescription from "../Invoice/InvoiceItemDescription";

const InvoiceItem = () => {
  const params = useParams();
  // 从Redux store获取发票列表(路径根据你的store结构调整)
  const invoices = useSelector(state => state.invoice.invoices);
  const invoice = invoices.find(
    (invoice) => invoice.id === params.invoiceId
  );

  // 空值判断:如果找不到发票,返回提示或404组件
  if (!invoice) {
    return <div>发票不存在或已被删除</div>;
  }

  return (
    <InvoiceItemDescription
      invoiceNumber={invoice.invoice_num}
      status={invoice.status}
      order_date={invoice.order_date}
      bill_from={invoice.bill_from}
      bill_from_address={invoice.bill_from_address}
      bill_from_email={invoice.bill_from_email}
      bill_from_fax={invoice.bill_from_fax}
      bill_from_phone={invoice.bill_from_phone}
      bill_to={invoice.bill_to}
      bill_to_address={invoice.bill_to_address}
      bill_to_email={invoice.bill_to_email}
      bill_to_fax={invoice.bill_to_fax}
      bill_to_phone={invoice.bill_to_phone}
      item_name={invoice.ITEMS.item_name}
      unit_costs={invoice.ITEMS.unit_costs}
      unit={invoice.ITEMS.unit}
      price={invoice.ITEMS.price}
    />
  );
};

export default InvoiceItem;

2. 持久化Redux状态(可选)

如果要解决刷新页面后状态丢失的问题,可以使用redux-persist库将Redux状态存储到本地存储,刷新后自动恢复状态。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 12:36:15