Home > Software design >  useEffect code not firing dispatch even though it works without?
useEffect code not firing dispatch even though it works without?

Time:09-25

I've been troubleshooting and I narrowed it down to the useEffect.

This code as-is won't fire the dispatch(getOrderByIdAction(id)) as shown in the redux dev tools.

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()
    // From Order Confirmation State 
    const orderDetails = useSelector((state) => state.orderConfirmation.orderDetails)
    const shippingAddress = orderDetails.shippingAddress
    const paymentMethod = orderDetails.paymentMethod
    const orderItems = orderDetails.orderItems

    // Calculator Order Summary
    const itemPrice = orderItems.reduce((acc, cur) => {
        return acc   (cur.price * cur.qty)
    }, 0)
    const taxPrice = Number((itemPrice * .1).toFixed(2))
    const totalPrice = taxPrice   itemPrice

    useEffect(() => {
        if (id) {
            dispatch(getOrderByIdAction(id))
        }
    },[])

    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>
                        {/* { error && <Message message={ "Hello" }/>} */}
                        <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, index) => 
                                    <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;

If I just run dispatch without useEffect, it will fire off with a success and I get all the order info on my reducer. So I know it has something to do with the useEffect.

Unfortunately, then I get an error that says orderDetails is undefined because I need useEffect to run it before the component renders?

Not sure what I did wrong

EDIT:

instructor's orderScreen.js

const OrderScreen = ({ match, history }) => {
  const orderId = match.params.id

  const [sdkReady, setSdkReady] = useState(false)

  const dispatch = useDispatch()

  const orderDetails = useSelector((state) => state.orderDetails)
  const { order, loading, error } = orderDetails

  const orderPay = useSelector((state) => state.orderPay)
  const { loading: loadingPay, success: successPay } = orderPay

  const orderDeliver = useSelector((state) => state.orderDeliver)
  const { loading: loadingDeliver, success: successDeliver } = orderDeliver

  const userLogin = useSelector((state) => state.userLogin)
  const { userInfo } = userLogin

  if (!loading) {
    //   Calculate prices
    const addDecimals = (num) => {
      return (Math.round(num * 100) / 100).toFixed(2)
    }

    order.itemsPrice = addDecimals(
      order.orderItems.reduce((acc, item) => acc   item.price * item.qty, 0)
    )
  }

  useEffect(() => {
    if (!userInfo) {
      history.push('/login')
    }

    const addPayPalScript = async () => {
      const { data: clientId } = await axios.get('/api/config/paypal')
      const script = document.createElement('script')
      script.type = 'text/javascript'
      script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}`
      script.async = true
      script.onload = () => {
        setSdkReady(true)
      }
      document.body.appendChild(script)
    }

    if (!order || successPay || successDeliver || order._id !== orderId) {
      dispatch({ type: ORDER_PAY_RESET })
      dispatch({ type: ORDER_DELIVER_RESET })
      dispatch(getOrderDetails(orderId))
    } else if (!order.isPaid) {
      if (!window.paypal) {
        addPayPalScript()
      } else {
        setSdkReady(true)
      }
    }
  }, [dispatch, orderId, successPay, successDeliver, order])

  const successPaymentHandler = (paymentResult) => {
    console.log(paymentResult)
    dispatch(payOrder(orderId, paymentResult))
  }

  const deliverHandler = () => {
    dispatch(deliverOrder(order))
  }

  return loading ? (
    <Loader />
  ) : error ? (
    <Message variant='danger'>{error}</Message>
  ) : ( jsx

edited code
OrderConfirmation.js

const OrderConfirmationScreen = () => {
    const dispatch = useDispatch()
    const { id } = useParams()
    // From Order Confirmation State 
    const orderConfirmation = useSelector((state) => state.orderConfirmation)
    const { loading, error, orderDetails } = orderConfirmation
   
    useEffect(() => {
        if(id) {
        console.log(id);
        dispatch(getOrderByIdAction(id))}
    },[id])

    const shippingAddress = orderDetails.shippingAddress
    const paymentMethod = orderDetails.paymentMethod
    const orderItems = orderDetails.orderItems

    // Calculator Order Summary
    const itemPrice = orderItems.reduce((acc, cur) => {
        return acc   (cur.price * cur.qty)
    }, 0)
    const taxPrice = Number((itemPrice * .1).toFixed(2))
    const totalPrice = taxPrice   itemPrice

    return  loading? (<h3>loading..</h3>) :
        ( jsx

CodePudding user response:

Since the data is not in the store when you render the component, you have to wait for the http request to complete.

You can return null when orderDetails is undefined, but consider adding a loading and error state to give a better user experience and make it easier for you to render a loading component or displaying an error

const OrderConfirmationScreen = () => {
    const dispatch = useDispatch();
    const { id } = useParams();
    // From Order Confirmation State
    const orderDetails = useSelector(
        state => state.orderConfirmation.orderDetails,
    );

    useEffect(() => {
        if (id) {
            dispatch(getOrderByIdAction(id));
        }
    }, [id]);

    // This is the important part.
    // consider replacing this condition with loading and error from your state
    if (!orderDetails) return <h3>Loading ...</h3>;

    const { shippingAddress, paymentMethod, orderItems } = orderDetails;

    // Calculator Order Summary
    const itemPrice = orderItems.reduce((acc, cur) => {
        return acc   cur.price * cur.qty;
    }, 0);
    const taxPrice = Number((itemPrice * 0.1).toFixed(2));
    const totalPrice = taxPrice   itemPrice;

    return jsx...
};
  • Related