React报错TypeError: 无法读取undefined的fullName属性
错误根本原因
你删除loading逻辑时只零散删除了部分代码,没有同步处理所有关联逻辑,导致组件首次渲染阶段就访问了空订单对象的嵌套属性:
- 初始状态下
order被定义为空对象{},原本的loading逻辑相当于渲染守卫:接口请求返回数据前只渲染加载组件,不会执行订单详情部分的代码,自然不会访问空order上不存在的shippingAddress、orderItems等属性。 - 你删代码时出现了逻辑断层:要么是删除了loading状态定义但没删掉渲染阶段的loading判断,导致
loading值为undefined,判断条件直接失效,提前进入详情渲染;要么是直接删掉了loading判断分支,但没有加新的渲染拦截,接口还没返回订单数据时就尝试读取order.shippingAddress.fullName,自然抛出undefined访问错误,页面白屏。 - 另外你还删掉了reducer中处理
FETCH_REQUESTaction的分支,但fetch请求函数里还保留了这个action的dispatch调用,会导致状态更新逻辑异常。
正确解决方法
如果你的目标是彻底移除loading相关逻辑,按以下步骤清理所有关联代码即可:
- 精简reducer,移除无用的FETCH_REQUEST分支
function reducer(state, action) { switch (action.type) { case 'FETCH_SUCCESS': return { ...state, order: action.payload, error: '' }; case 'FETCH_FAIL': return { ...state, error: action.payload }; default: return state; } }
- 调整useReducer定义,移除所有loading相关的字段
const [{ error, order }, dispatch] = useReducer(reducer, { order: {}, error: '', });
- 清理useEffect中的请求逻辑,移除FETCH_REQUEST的dispatch调用
useEffect(() => { const fetchOrder = async () => { try { const { data } = await axios.get(`/api/orders/${orderId}`, { headers: { authorization: `Bearer ${userInfo.token}` }, }); dispatch({ type: 'FETCH_SUCCESS', payload: data }); } catch (err) { dispatch({ type: 'FETCH_FAIL', payload: getError(err) }); } }; if (!userInfo) { return navigate('/login'); } if (!order._id || (order._id && order._id !== orderId)) { fetchOrder(); } }, [order, userInfo, orderId, navigate]);
- 修改渲染逻辑,移除原有loading判断,新增数据加载守卫:接口返回有效订单数据前,不渲染详情部分,避免访问空属性
// 先处理错误状态 if (error) { return <MessageBox variant="danger">{error}</MessageBox> } // 数据未加载完成时不渲染详情,可按需替换成你想要的占位内容 if (!order._id) { return null } // 以下是原有的订单详情渲染逻辑,保持不变即可 return ( <div> <Helmet> <title>Order {orderId}</title> </Helmet> <h1 className="my-3">Order {orderId}</h1> <Row> <Col md={8}> <Card className="mb-3"> <Card.Body> <Card.Title>Shipping</Card.Title> <Card.Text> <strong>Name:</strong> {order.shippingAddress.fullName} <br /> <strong>Address: </strong> {order.shippingAddress.address}, {order.shippingAddress.city}, {order.shippingAddress.postalCode} ,{order.shippingAddress.country} </Card.Text> {order.isDelivered ? ( <MessageBox variant="success"> Delivered at {order.deliveredAt} </MessageBox> ) : ( <MessageBox variant="danger">Not Delivered</MessageBox> )} </Card.Body> </Card> <Card className="mb-3"> <Card.Body> <Card.Title>Payment</Card.Title> <Card.Text> <strong>Method:</strong> {order.paymentMethod} </Card.Text> {order.isPaid ? ( <MessageBox variant="success"> Paid at {order.paidAt} </MessageBox> ) : ( <MessageBox variant="danger">Not Paid</MessageBox> )} </Card.Body> </Card> <Card className="mb-3"> <Card.Body> <Card.Title>Items</Card.Title> <ListGroup variant="flush"> {order.orderItems.map((item) => ( <ListGroup.Item key={item._id}> <Row className="align-items-center"> <Col md={6}> <img src={item.image} alt={item.name} className="img-fluid rounded img-thumbnail" ></img>{' '} <Link to={`/product/${item.slug}`}>{item.name}</Link> </Col> <Col md={3}> <span>{item.quantity}</span> </Col> <Col md={3}>${item.price}</Col> </Row> </ListGroup.Item> ))} </ListGroup> </Card.Body> </Card> </Col> <Col md={4}> <Card className="mb-3"> <Card.Body> <Card.Title>Order Summary</Card.Title> <ListGroup variant="flush"> <ListGroup.Item> <Row> <Col>Items</Col> <Col>${order.itemsPrice.toFixed(2)}</Col> </Row> </ListGroup.Item> <ListGroup.Item> <Row> <Col>Shipping</Col> <Col>${order.shippingPrice.toFixed(2)}</Col> </Row> </ListGroup.Item> <ListGroup.Item> <Row> <Col>Tax</Col> <Col>${order.taxPrice.toFixed(2)}</Col> </Row> </ListGroup.Item> <ListGroup.Item> <Row> <Col> <strong> Order Total</strong> </Col> <Col> <strong>${order.totalPrice.toFixed(2)}</strong> </Col> </Row> </ListGroup.Item> </ListGroup> </Card.Body> </Card> </Col> </Row> </div> );
如果你原本不想删除loading功能,只需要把你标记了// Deleted的所有loading相关代码恢复即可,原有逻辑本身可以正常运行,loading状态会在数据加载完成前拦截详情渲染,不会出现属性访问错误。
内容的提问来源于stack exchange,提问作者CheMansur
相关产品推荐
相关产品推荐

