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

状态变更时如何更新Stripe PaymentRequest的'on'事件监听器?

Stripe PaymentRequestButton 事件监听不随状态更新的解决方案

我在用Stripe的PaymentRequestButton实现Apple Pay和Google Pay功能,需要创建Stripe的PaymentRequest对象并通过PaymentRequest.on('paymentmethod', ...)监听事件。这段逻辑放在依赖amount等状态的useEffect里,但修改amount时事件逻辑不会更新,始终使用组件挂载时的状态值,尝试过卸载、清理函数都没效果,希望能更新对应的事件监听。

原代码:

let pr = null;

const StripeCheckoutForm = ({ amount, subscription, websiteName }) => {
  const stripe = useStripe();

  const [paymentRequest, setPaymentRequest] = useState("");
  const [errorMessage, setErrorMessage] = useState("");

  useEffect(() => {
    if (stripe) {
      pr = stripe.paymentRequest({
        country: "CA",
        currency: "cad",
        requestPayerName: true,
        requestPayerEmail: true,
        total: { label: websiteName, amount: 1000 },
      });

      pr.canMakePayment().then((result) => {
        if (result) {
          setPaymentRequest(pr);
        }
      });

      // 此代码块内容不会随状态变更而改变
      // 它始终使用挂载时的状态值运行
      // 我尝试过卸载、清理函数等方法,但均无效

      pr.on("paymentmethod", async (e) => {
       
          const functions = firebase.functions();
          console.log(subscription);
          if (subscription) {
            console.log("Subscription");
           
            e.complete("success");
          } else {
            console.log("One time");
            
          }
      
      });

      pr.on("cancel", async (e) => {
        setErrorMessage("Payment box cancelled");
      });
    }
  }, [stripe, subscription, amount, websiteName]);

  return (
    <div className="stripe-checkout-form-component">
      <div className="payment-request-buttons-container">
        {paymentRequest && (
          <PaymentRequestButtonElement options={{ paymentRequest }} />
        )}
        <div className="payment-request-error">{errorMessage}</div>
      </div>
    </div>
  );
};

问题根源

  1. 全局变量pr会导致多组件实例互相干扰,且无法正确追踪状态变更。
  2. 每次useEffect触发时创建新的PaymentRequest对象,但旧对象的事件监听未被移除,且新监听捕获的是当前闭包的状态值,组件状态更新后闭包不会同步更新。
  3. total的amount写死为1000,未使用传入的amount参数。

修正后的代码

const StripeCheckoutForm = ({ amount, subscription, websiteName }) => {
  const stripe = useStripe();
  const [paymentRequest, setPaymentRequest] = useState(null);
  const [errorMessage, setErrorMessage] = useState("");
  // 用useRef存储PaymentRequest实例,方便在清理函数中访问
  const prRef = useRef(null);

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

    // 创建新的PaymentRequest对象,使用最新的状态参数
    const pr = stripe.paymentRequest({
      country: "CA",
      currency: "cad",
      requestPayerName: true,
      requestPayerEmail: true,
      total: { label: websiteName, amount },
    });

    prRef.current = pr;

    // 检查支付支持状态并更新组件状态
    pr.canMakePayment().then((result) => {
      setPaymentRequest(result ? pr : null);
    });

    // 定义事件处理函数
    const handlePaymentMethod = async (e) => {
      const functions = firebase.functions();
      console.log(subscription);
      if (subscription) {
        console.log("Subscription");
        // 添加订阅支付逻辑
        e.complete("success");
      } else {
        console.log("One time");
        // 添加一次性支付逻辑
      }
    };

    const handleCancel = async (e) => {
      setErrorMessage("Payment box cancelled");
    };

    // 绑定事件监听
    pr.on("paymentmethod", handlePaymentMethod);
    pr.on("cancel", handleCancel);

    // 清理函数:移除旧监听,避免内存泄漏和旧逻辑触发
    return () => {
      if (prRef.current) {
        prRef.current.off("paymentmethod", handlePaymentMethod);
        prRef.current.off("cancel", handleCancel);
        prRef.current = null;
      }
      setPaymentRequest(null);
    };
  }, [stripe, subscription, amount, websiteName]);

  return (
    <div className="stripe-checkout-form-component">
      <div className="payment-request-buttons-container">
        {paymentRequest && (
          <PaymentRequestButtonElement options={{ paymentRequest }} />
        )}
        <div className="payment-request-error">{errorMessage}</div>
      </div>
    </div>
  );
};

关键修改点

  • 移除全局变量:改用useRef存储PaymentRequest实例,确保组件实例独立,且能在清理阶段访问当前实例。
  • 同步状态参数:total.amount改为使用传入的amount参数,确保金额随状态实时更新。
  • 添加清理逻辑:在useEffect返回的清理函数中,用off方法移除旧事件监听,避免旧逻辑持续触发,同时清空组件状态。
  • 提取处理函数:将事件逻辑提取为独立函数,确保清理时能精准移除对应监听。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 18:35:41