0

So I am building an e-commerce website checkout page with commerce.js. I have a context that allows me to use the cart globally. But on the checkout page when I generate the token inside useEffect , the cart variables have not been set until then.

My context is as below

import { createContext, useEffect, useContext, useReducer } from 'react';
import { commerce } from '../../lib/commerce';

//Provides a context for Cart to be used in every page

const CartStateContext = createContext();
const CartDispatchContext = createContext();

const SET_CART = 'SET_CART';

const initialState = {
  id: '',
  total_items: 0,
  total_unique_items: 0,
  subtotal: [],
  line_items: [{}],
};

const reducer = (state, action) => {
  switch (action.type) {
    case SET_CART:
      return { ...state, ...action.payload };
    default:
      throw new Error(`Unknown action: ${action.type}`);
  }
};

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const setCart = (payload) => dispatch({ type: SET_CART, payload });

  useEffect(() => {
    getCart();
  }, []);

  const getCart = async () => {
    try {
      const cart = await commerce.cart.retrieve();
      setCart(cart);
    } catch (error) {
      console.log('error');
    }
  };

  return (
    <CartDispatchContext.Provider value={{ setCart }}>
      <CartStateContext.Provider value={state}>
        {children}
      </CartStateContext.Provider>
    </CartDispatchContext.Provider>
  );
};

export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);

Now on my checkout page

const CheckoutPage = () => {
  const [open, setOpen] = useState(false);
  const [selectedDeliveryMethod, setSelectedDeliveryMethod] = useState(
    deliveryMethods[0]
  );

  const [checkoutToken, setCheckoutToken] = useState(null);
  const { line_items, id } = useCartState();

  useEffect(() => {
    const generateToken = async () => {
      try {
        const token = await commerce.checkout.generateToken(id, {
          type: 'cart',
        });
        setCheckoutToken(token);
      } catch (error) {}
    };
    console.log(checkoutToken);
    console.log(id);

    generateToken();
  }, []);

  return <div> {id} </div>; //keeping it simple just to explain the issue
};

In the above code id is being rendered on the page, but the token is not generated since on page load the id is still blank. console.log(id) gives me blank but {id} gives the actual value of id

lpizzinidev
  • 12,741
  • 2
  • 10
  • 29
rohit
  • 159
  • 12
  • 1
    Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – geisterfurz007 Aug 05 '22 at 07:31
  • hey thanks man, this thread definitely helps – rohit Aug 05 '22 at 08:06

1 Answers1

1

Because CheckoutPage is a child of CartProvider, it will be mounted before CartProvider and the useEffect will be called in CheckoutPage first, so the getCart method in CartProvider hasn't been yet called when you try to read the id inside the useEffect of CheckoutPage. I'd suggest to try to call generateToken each time id changes and check if it's initialised first.

useEffect(() => {
    if (!id) return;

    const generateToken = async () => {
    try{
      

        const token = await commerce.checkout.generateToken(id, {type: 'cart'})
        setCheckoutToken(token)

    } catch(error){

      }
  }
  console.log(checkoutToken)
  console.log(id)


  generateToken()   
  
}, [id]);
ddm
  • 100
  • 2