状态变更时如何更新Stripe PaymentRequest的'on'事件监听器?
我在用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> ); };
问题根源
- 全局变量
pr会导致多组件实例互相干扰,且无法正确追踪状态变更。 - 每次
useEffect触发时创建新的PaymentRequest对象,但旧对象的事件监听未被移除,且新监听捕获的是当前闭包的状态值,组件状态更新后闭包不会同步更新。 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
相关产品推荐
相关产品推荐

