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

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相关逻辑,按以下步骤清理所有关联代码即可:

  1. 精简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;
  }
}
  1. 调整useReducer定义,移除所有loading相关的字段
const [{ error, order }, dispatch] = useReducer(reducer, {
  order: {},
  error: '',
});
  1. 清理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]);
  1. 修改渲染逻辑,移除原有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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 23:01:09