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

React TS2786报错:Card无法作为JSX组件使用问题咨询

错误根因

React 函数组件不支持定义为 async 异步函数:

  • async 函数的返回值永远是 Promise 类型,不符合 React 对组件返回值必须是同步 JSX 元素/null 的要求,这就是 TS2786 报错的直接来源
  • 你当前在组件函数体顶部直接 await AdyenCheckout(configuration) 的写法,就算绕过 TS 校验,运行时也会因为组件返回 Promise 导致 React 渲染失败
修复步骤
  • 移除组件定义的 async 修饰符,让组件保持同步返回 JSX 的标准结构
  • 将 Adyen 实例初始化、挂载的异步逻辑迁移到 useEffect 中执行(useEffect 执行时 DOM 已经完成渲染,可以正常获取挂载容器节点)
  • 使用 useRef 存储 Adyen checkout、卡组件实例,避免重复初始化,同时在组件卸载时调用实例的 unmount 方法做内存清理
修复后可运行代码
import { useEffect, useRef } from 'react';
import AdyenCheckout from '@adyen/adyen-web';
import type { CbObjOnBinLookup, AdyenCheckoutError, CardState } from '@adyen/adyen-web';

const Card = () => {
  // 存储容器和实例引用
  const cardContainerRef = useRef<HTMLDivElement>(null);
  const checkoutRef = useRef<Awaited<ReturnType<typeof AdyenCheckout>> | null>(null);
  const cardComponentRef = useRef<ReturnType<Awaited<ReturnType<typeof AdyenCheckout>>['create']> | null>(null);

  useEffect(() => {
    // 异步初始化逻辑放到effect中
    const initAdyenCard = async () => {
      if (!cardContainerRef.current) return;

      const handleOnChange = (state: CardState, component: unknown) => {
        state.isValid; // 校验用户输入是否全部合法
        state.data; // 调用/payments接口需要传的支付数据
        component; // 触发事件的当前组件实例
      };

      const configuration = {
        locale: "en_US",
        environment: "test",
        clientKey: "YOUR_CLIENT_KEY",
        onChange: handleOnChange
      };

      const checkout = await AdyenCheckout(configuration);
      checkoutRef.current = checkout;

      cardComponentRef.current = checkout.create('securedfields', {
        type: 'card',
        brands: ['mc', 'visa', 'amex', 'bcmc', 'maestro'],
        styles: {
          error: { color: 'red' },
          validated: { color: 'green' },
          placeholder: { color: '#d8d8d8' }
        },
        ariaLabels: {
          lang: 'en-GB',
          encryptedCardNumber: {
            label: 'Credit or debit card number field',
            iframeTitle: 'Iframe for secured card number',
            error: 'Message that gets read out when the field is in the error state'
          }
        },
        onChange: () => {},
        onValid: () => {},
        onLoad: () => {},
        onConfigSuccess: () => {},
        onFieldValid: () => {},
        onBrand: () => {},
        onError: (error: AdyenCheckoutError) => {
          console.error('Adyen card component error:', error);
        },
        onFocus: () => {},
        onBinValue: (bin: string) => {},
        onBinLookup: (callbackObj: CbObjOnBinLookup) => {}
      // 直接挂载到ref对应的DOM节点,不需要依赖id查询
      }).mount(cardContainerRef.current);
    };

    initAdyenCard();

    // 组件卸载时清理实例
    return () => {
      cardComponentRef.current?.unmount();
      checkoutRef.current = null;
      cardComponentRef.current = null;
    };
  }, []);

  return (
    <div ref={cardContainerRef}>
      <label>
        <span>Card number:</span>
        <span data-cse="encryptedCardNumber"></span>
      </label>
      <label>
        <span>Expiry date:</span>
        <span data-cse="encryptedExpiryDate"></span>
      </label>
      <label>
        <span>CVV/CVC:</span>
        <span data-cse="encryptedSecurityCode"></span>
      </label>
    </div>
  );
};

export default Card;
额外注意点
  • 不要再给组件外层套空 Fragment 后再写带id的容器,直接给容器绑定ref更可靠,避免id重复、DOM查询时机不对的问题
  • 如果你使用的 Adyen Web 版本 >=5.0.0,官方已经提供了React封装包,不需要手动挂载实例,直接用封装好的组件可以减少这类生命周期问题
  • 替换代码里的YOUR_CLIENT_KEY为你自己的真实客户端密钥,test环境要注意对应测试密钥的权限配置

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 10:30:53