如何在 React component 中运行 library callback function
结论
这 4 个函数通常不需要由 React 主动调用。你需要把它们注册给支付 library,再由支付流程在失败、取消、完成或超时时触发。
如果 library 支持传入 callback,可以直接使用 React component 中定义的函数。如果它只识别固定的全局函数名,则需要在 component 挂载时将这些函数注册到 window。支付完成后,更新 React state 即可显示成功 dialog。
推荐方式:将 callback 传给支付 library
如果支付 library 的初始化 API 接受 callback 配置,可以这样写:
import { useState } from 'react';
function Payment() {
const [paymentStatus, setPaymentStatus] = useState('idle');
function errorCallback(error) {
console.error('Payment error:', error);
setPaymentStatus('error');
}
function cancelCallback() {
console.log('Payment cancelled');
setPaymentStatus('cancelled');
}
function completeCallback(result) {
console.log('Payment complete:', result);
setPaymentStatus('complete');
}
function timeoutCallback() {
console.log('Payment timeout');
setPaymentStatus('timeout');
}
function startPayment() {
// 以下名称和参数仅为结构示例,
// 请替换成该 payment gateway 实际提供的 API。
window.PaymentGateway.open({
onError: errorCallback,
onCancel: cancelCallback,
onComplete: completeCallback,
onTimeout: timeoutCallback,
});
}
return (
<div>
<button type="button" onClick={startPayment}>
Pay now
</button>
{paymentStatus === 'complete' && (
<div role="dialog" aria-modal="true" aria-labelledby="payment-title">
<h2 id="payment-title">支付成功</h2>
<p>付款已经完成。</p>
<button
type="button"
onClick={() => setPaymentStatus('idle')}
>
关闭
</button>
</div>
)}
</div>
);
}
export default Payment;
支付完成时,completeCallback 会执行:
setPaymentStatus('complete');
state 更新后,React 会重新渲染 component,成功 dialog 随即显示。
其中的 PaymentGateway.open、onComplete 等名称只是示例。问题中没有说明具体使用哪个支付服务及其 API,因此需要根据该 library 的文档替换这些名称。
如果 library 要求全局 callback
一些旧式支付 library 不接受函数引用,只会调用页面上的 completeCallback、errorCallback 等全局函数。React component 中声明的函数不会自动成为全局函数,需要手动挂载到 window:
import { useEffect, useState } from 'react';
function Payment() {
const [paymentStatus, setPaymentStatus] = useState('idle');
const [paymentError, setPaymentError] = useState(null);
useEffect(() => {
window.errorCallback = function errorCallback(error) {
console.error('Payment error:', error);
setPaymentError(error);
setPaymentStatus('error');
};
window.cancelCallback = function cancelCallback() {
console.log('Payment cancelled');
setPaymentStatus('cancelled');
};
window.completeCallback = function completeCallback(result) {
console.log('Payment complete:', result);
setPaymentStatus('complete');
};
window.timeoutCallback = function timeoutCallback() {
console.log('Payment timeout');
setPaymentStatus('timeout');
};
return () => {
delete window.errorCallback;
delete window.cancelCallback;
delete window.completeCallback;
delete window.timeoutCallback;
};
}, []);
function closeDialog() {
setPaymentStatus('idle');
setPaymentError(null);
}
return (
<section>
{paymentStatus === 'complete' && (
<div role="dialog" aria-modal="true" aria-labelledby="success-title">
<h2 id="success-title">支付成功</h2>
<p>订单付款已完成。</p>
<button type="button" onClick={closeDialog}>
确定
</button>
</div>
)}
{paymentStatus === 'error' && (
<div role="alert">
支付失败,请稍后重试。
{paymentError?.message && <p>{paymentError.message}</p>}
</div>
)}
</section>
);
}
export default Payment;
component 挂载后,useEffect 会注册这些全局函数。component 卸载时,清理函数会将它们删除,防止 callback 残留或重复注册。
如果项目使用 TypeScript,还需要声明这些全局属性:
declare global {
interface Window {
errorCallback?: (error: unknown) => void;
cancelCallback?: () => void;
completeCallback?: (result?: unknown) => void;
timeoutCallback?: () => void;
}
}
确保外部 library 已经加载
如果通过 index.html 引入支付脚本,需要确认脚本地址正确,并根据实际情况使用 defer:
<script src="https://example.com/payment-library.js" defer></script>
调用 API 前也可以先检查 library 是否存在,以免脚本尚未加载就执行调用:
function startPayment() {
if (!window.PaymentGateway) {
console.error('Payment library has not been loaded');
return;
}
window.PaymentGateway.open();
}
能否使用 defer 取决于该 library 的加载要求。如果它必须在页面解析期间立即执行,或者依赖其他脚本的加载顺序,应按照支付服务的接入文档处理。
注意事项
- 优先使用 library 提供的 callback 参数或事件订阅 API。只有在 library 明确要求固定函数名时,才使用全局
windowcallback。 - callback 的实际名称、参数和返回值应以支付服务文档为准。
- 不要只根据前端的
completeCallback判断订单已经可靠支付。正式业务中,应由服务端接收并验证支付网关的通知,再更新订单状态。 - 如果支付 component 可能同时挂载多个实例,不应共用同一组
windowcallback,否则后注册的函数会覆盖先注册的函数。 - dialog 的显示状态应由 React state 管理,不要在 callback 中直接操作 DOM。
备注:内容仅供参考。
