React遍历order的orderItems与shippingAddress报错如何解决
React订单组件读取属性报错排查方案
问题场景
需要遍历订单返回数据中的orderItems订单项、shippingAddress收货地址字段,接口返回的原始数据结构如下:
{ "message": "New Order Created", "order": { "createdAt": "2022-06-02T02:19:26.124Z", "isDelivered": false, "isPaid": false, "orderItems": [ {"name": "4Formaggi", "quantity": 1, "image": "pics/4formagi.jpg", "price": 30}, {"name": "4Stagioni", "quantity": 1, "image": "pics/4stagioni.jpg", "price": 30} ], "paymentMethod": "paypal", "shippingAddress": {"fname": "Marius-Daniel Ciufu", "city": "Baicoi", "postal": "105200", "county": "Prahova", "address": "Baicoi"}, "totalPrice": 60, "updatedAt": "2022-06-02T02:19:26.124Z", "user": "6297ef1744555d10ed5a704b", "__v": 0, "_id": "62981e2e4fa20d980c6f3a29" } }
尝试通过order.orderItems.map((item) => ...)实现遍历逻辑时,控制台抛出如下错误:
Order.js:54 Uncaught TypeError: Cannot read properties of undefined (reading 'fname') react-dom.development.js:18525 The above error occurred in the <Order> component:
错误指向Order.js第54行,提示无法读取undefined值的fname属性。
根因分析
- 组件首次渲染时,订单数据还处于异步请求阶段,定义的
order状态初始值为undefined,此时直接访问order.shippingAddress.fname,相当于从undefined上读取属性,直接触发类型错误。绝大多数这类React异步数据渲染报错,都是数据未就绪时提前访问深层属性导致的 - 部分场景下是取值路径错误:接口返回的订单详情嵌套在
order字段下,如果直接把整个接口返回体存在state里,写order.orderItems实际访问的是接口返回值.order.orderItems,路径不匹配会拿到undefined - 即使
order对象已存在,加载过程中shippingAddress字段可能还未完成赋值,直接深层读取属性也会触发同类报错
注意:不要依赖"数据加载很快所以不会触发首次渲染空值"的侥幸,只要是异步获取的数据,组件首次渲染一定是先跑空状态的渲染逻辑,再在数据返回后触发重渲染,不做兜底判断100%有概率触发报错。
修复方案
- 给属性访问加可选链与空值兜底,避免未加载完成时执行属性读取逻辑
// 渲染收件人信息时加可选链判断,属性不存在时不渲染对应块 {order?.shippingAddress?.fname && ( <div className="shipping-info"> <p>收件人:{order.shippingAddress.fname}</p> <p>地址:{order.shippingAddress.county} {order.shippingAddress.city} {order.shippingAddress.address}</p> <p>邮编:{order.shippingAddress.postal}</p> </div> )} // 遍历订单项时给数组兜底,避免undefined上调用map方法报错 {(order?.orderItems || []).map((item) => ( <div className="order-item" key={item._id}> <img src={item.image} alt={item.name} /> <p>商品名称:{item.name}</p> <p>数量:{item.quantity}</p> <p>单价:¥{item.price}</p> </div> ))} - 校验接口取值路径:异步请求拿到响应后,确认把内层的订单对象存入state,不要存整个接口返回体
// 错误写法:把整个响应体存在order里 // const res = await axios.get('/api/order/xxx') // setOrder(res.data) // 正确写法:取响应里的order字段存入state setOrder(res.data.order) - 给state设置合理初始值,从根源避免初始值为undefined的问题
// 定义订单state时直接匹配数据结构给初始值 const [order, setOrder] = useState({ orderItems: [], shippingAddress: {}, totalPrice: 0, paymentMethod: '', isPaid: false, isDelivered: false })
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

