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

React useEffect中购物车ID未定义导致Checkout Token生成失败

解决Commerce.js + React Context购物车ID初始化延迟导致无法生成Checkout Token的问题

问题根源

你的代码存在异步操作的时序冲突:

  • CartProvider 中的 getCart 是异步请求,组件挂载后才发起,需要一定时间返回数据并更新购物车状态。
  • 结账页面的 useEffect 依赖项为空数组,仅在组件首次挂载时执行一次,此时购物车id还是初始状态的空字符串,导致生成Token失败。而页面能渲染出id是因为后续购物车数据更新触发组件重渲染,此时id已有有效值。

解决方案

方案一:监听购物车ID变化,有效时生成Token

修改结账页面的useEffect,添加id作为依赖项,并在生成Token前校验id的有效性:

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 () => {
      // 跳过空ID的情况,避免无效请求
      if (!id) return;
      try {
        const token = await commerce.checkout.generateToken(id, {
          type: 'cart',
        });
        setCheckoutToken(token);
      } catch (error) {
        console.error('生成Checkout Token失败:', error);
      }
    };

    generateToken();
  }, [id]); // 当id变化时重新执行副作用

  return <div> {id} </div>;
};

方案二:等待购物车加载完成后再渲染结账页面

在CartProvider中添加加载状态,确保购物车数据完全加载后再处理结账逻辑:

1. 修改CartProvider代码

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

const CartStateContext = createContext();
const CartDispatchContext = createContext();
const CartLoadingContext = 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 [isLoading, setIsLoading] = useState(true); // 购物车加载状态
  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);
    } finally {
      setIsLoading(false); // 请求结束后标记加载完成
    }
  };

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

export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);
export const useCartLoading = () => useContext(CartLoadingContext); // 导出加载状态钩子

2. 在结账页面使用加载状态

const CheckoutPage = () => {
  const isLoading = useCartLoading();
  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.error('生成Checkout Token失败:', error);
      }
    };

    // 购物车加载完成后再生成Token
    if (!isLoading) {
      generateToken();
    }
  }, [id, isLoading]);

  // 加载中显示提示
  if (isLoading) {
    return <div>加载中...</div>;
  }

  return <div> {id} </div>;
};

内容的提问来源于stack exchange,提问作者rohit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 09:36:20