useEffect内dispatch(getOrderByIdAction)未触发,单独调用正常但报orderDetails未定义
问题核心原因
- useEffect执行时机认知错误:React 的
useEffect钩子是在组件首次渲染完成后才会执行,并非你预期的渲染前执行。你原有代码在组件顶层逻辑直接访问orderDetails.shippingAddress、orderDetails.paymentMethod等子属性,此时useEffect还没触发dispatch拉取数据,Redux 中存储的orderDetails还是初始空值/undefined,直接读取子属性必然抛出未定义错误。 - 原有useEffect依赖配置错误:你最初的
useEffect依赖数组为空,只会在组件首次挂载时执行一次。如果useParams获取的id是异步更新的,首次执行时id为 undefined,就会被if(id)拦截,不会触发dispatch,这就是你在Redux DevTools看不到对应action的原因。 - 修改后的代码逻辑顺序错误:你调整后的代码虽然加了loading判断、把id加到了依赖数组,但还是在return之前就直接访问
orderDetails的子属性,loading为true时这些逻辑仍会执行,还是会抛出未定义错误。
正确修复方案
- 先给Redux的
orderConfirmation模块设置合理初始值:初始loading设为true,orderDetails设为null - 调整逻辑顺序,先判断loading、error、orderDetails是否存在,再执行依赖orderDetails的计算和渲染逻辑
useEffect依赖数组补充dispatch,符合React Hooks语法规则
修正后代码示例
import { useDispatch, useSelector } from "react-redux"; import { useEffect } from "react"; import { useParams, Link } from "react-router-dom"; import CheckoutSteps from '../components/CheckoutSteps' import { getOrderByIdAction } from '../state/actions/orderActions' import Message from '../components/Message' const OrderConfirmationScreen = () => { const dispatch = useDispatch() const { id } = useParams() // 从Redux取状态,需提前配置初始值:{ loading: true, error: null, orderDetails: null } const orderConfirmation = useSelector((state) => state.orderConfirmation) const { loading, error, orderDetails } = orderConfirmation useEffect(() => { if(id) { dispatch(getOrderByIdAction(id)) } },[id, dispatch]) // 前置判断,提前return阻断后续逻辑执行 if (loading) return <h3>加载中...</h3> if (error) return <Message message={error} /> if (!orderDetails) return null // 只有orderDetails存在时才会执行下面的逻辑,不会报错 const shippingAddress = orderDetails.shippingAddress const paymentMethod = orderDetails.paymentMethod const orderItems = orderDetails.orderItems // 计算订单金额 const itemPrice = orderItems.reduce((acc, cur) => acc + (cur.price * cur.qty), 0) const taxPrice = Number((itemPrice * .1).toFixed(2)) const totalPrice = taxPrice + itemPrice return ( <div className="main-container lg:p-16"> <CheckoutSteps step1 step2 step3 /> <div className="max-w-6xl mx-auto p-8 my-8 w-full"> <div className="flex flex-col lg:flex-row"> <div className="place-order-container bg-gray-50 rounded-md shadow-2xl m-2 p-6 lg:p-12 w-full"> <h1 className="text-center font-semibold text-xl mt-4 mb-6">Place Order</h1> <div className="order-header-group grid grid-cols-2 bg-white rounded-lg shadow-xl pt-4 pb-8 px-2"> <div className="order-shipping-section my-2 mx-4 lg:p-2"> <h4 className="font-medium my-4">Shipping Address: </h4> <div className="address-group"> <h6 className="text-sm">{shippingAddress.address}</h6> <h6 className="text-sm">{shippingAddress.city}, {shippingAddress.zipcode}</h6> <h6 className="text-sm">{shippingAddress.phone}</h6> { shippingAddress && <Link to={'/shipping'} className="underline my-2 block">Edit</Link> } </div> </div> <div className="order-payment-section my-2 lg:p-2"> <h4 className="font-medium my-4">Payment Method: </h4> { paymentMethod === 'Paypal' && <i className="fab fa-paypal fa-lg mr-2" style={{color: "#253B80"}}></i>} <span className="text-sm">{paymentMethod}</span> </div> </div> <div className="order-items-group my-3 bg-white rounded-lg shadow-2xl p-2 lg:p-4"> <div className="order-items-group rounded-md p-6"> { orderItems.map((item) => <div key={item.productId} className="grid grid-cols-8 gap-2 items-center my-4"> <img src={item.image} alt={item.name} className="col-span-2 w-40 p-4" /> <h6 className="col-span-3 text-sm">{item.name}</h6> <h6 className="col-span-1 text-sm text-center">{item.qty}</h6> <h6 className="col-span-1 text-sm text-center">x</h6> <h6 className="col-span-1 text-sm text-center">{item.price}</h6> <hr className="my-4 col-span-8"/> </div> )} </div> </div> </div> <div className="order-total-section bg-gray-50 rounded-md shadow-2xl m-2 p-6 lg:pb-8 w-full lg:w-2/5 h-1/2"> <h4 className="font-semibold text-xl text-center my-8">Order Summary</h4> <div className="bg-white rounded-lg shadow-2xl p-4"> <div className="items-total grid grid-cols-3 p-1"> <span className="col-span-2 my-2 block text-sm">Items: </span> <span className="col-span-1 my-2 block text-sm">${itemPrice} </span> </div> <div className="shipping-total grid grid-cols-3 p-1"> <span className="col-span-2 my-2 block text-sm">Shipping: </span> <span className="col-span-1 my-2 block text-sm">FREE </span> </div> <div className="tax-total grid grid-cols-3 p-1"> <span className="col-span-2 my-2 block text-sm">Tax: </span> <span className="col-span-1 my-2 block text-sm">${taxPrice} </span> </div> <hr className="my-2" /> <div className="total-total grid grid-cols-3 p-1"> <span className="col-span-2 my-2 block text-sm font-semibold">Total: </span> <span className="col-span-1 my-2 block text-sm font-semibold">${totalPrice} </span> </div> </div> </div> </div> </div> </div> ); }; export default OrderConfirmationScreen;
内容的提问来源于stack exchange,提问作者pancake
相关产品推荐
相关产品推荐

