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;
问题原因
- 数据源不统一:
InvoiceItem.js里从静态的INVOICES_LIST查找发票,但新增的发票只存在Redux store中,静态列表没有新增条目,find方法返回undefined,后续访问invoice.invoice_num就会触发报错。 - 刷新状态丢失:刷新页面后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
相关产品推荐
相关产品推荐

