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

如何将React类组件形式的Braintree支付组件转为函数组件?

React/MUI中Braintree支付的类组件转函数组件方案

我在React/MUI应用中集成Braintree支付,现有可行的类组件实现,想要转成规范的函数组件。转换时遇到两个问题:

  • 不知道如何替代componentDidMount生命周期
  • 不清楚函数组件中如何处理Braintree的instance实例

以下是原类组件代码和我的尝试代码:

原类组件代码

const BraintreeDropInPaymentMethod = () => {
    class Store extends React.Component {
        instance;

        state = {
            clientToken: '<BRAIN TREE KEY>'
        };

        async componentDidMount() {
            const response = await fetch("server.test/client_token");
            const clientToken = await response.json();

            this.setState({
                clientToken,
            });
        }

        async buy() {
            const { nonce } = await this.instance.requestPaymentMethod();
            await fetch(`server.test/purchase/${nonce}`);
        }

        render() {
            if (!this.state.clientToken) {
                return (
                    <div>
                        <h1>Loading...</h1>
                    </div>
                );
            } else {
                return (
                    <div>
                        <DropIn
                            options={{ authorization: this.state.clientToken }}
                            onInstance={(instance) => (this.instance = instance)}
                        />
                        <Button
                            variant='contained'
                            onClick={this.buy.bind(this)}
                        >
                            Create Account
                        </Button>
                        <Button
                            variant='outlined'
                            sx={{ marginLeft: 3 }}
                            color='warning'
                            onClick={(e) => handleCancelAccountCreation(e)}
                            href='/store-front'
                        >
                            Cancel
                        </Button>
                    </div>
                );
            }
        }
    }

    const [user, setUser] = useState({})
    const handleCancelAccountCreation = (event) => {
        setUser({})
        document.getElementById('signInBtn').hidden = false
    }

    return (
        <Store/>
    )
}

我的尝试代码

const BraintreeDropInPaymentMethod = () => {

    const [token, setToken] = useState('<BRAIN TREE KEY>')
    const [user, setUser] = useState({})

    const contactServer = async () => {
        const res = await fetch('server.test/client_token')
        const clientToken = await res.json()
        console.log(clientToken)
        setToken(token)
    }

    const buy = async () => {
        const { nonce } = await this.instance.requestPaymentMethod()
        await fetch(`server.test/purchase/${nonce}`)
    }

    const handleCancelAccountCreation = (event) => {
        setUser({})
        document.getElementById('signInBtn').hidden = false
    }

    const createAccountOptions = () => {
        if (!token) {
            return (
                <div>
                    <h1>Loading...</h1>
                </div>
            ) else {
                return (
                    <div>
                        <DropIn
                            options={ authorization: {setToken})
                            onInstance={(instance) => (this.instance = instance)}
                        />
                        <Button
                            variant="contained'
                            onClick={buy}
                        >
                         Create Account
                        </Button
                            variant='outlined'
                            sx={{ marginLeft: 3 }}
                            color='warning'
                            onClick={(e) => handleCancelAccountCreation(e)}
                            href='/store-front'
                        >
                        <Button>
                            Cancel
                        </Button>
                    </div>
                )
            }
        }
    }

    return(
        <>
            <createAccountOptions/>
        </>
    )   
}

关键问题解决

1. 替代componentDidMount

函数组件中用useEffect钩子,传入空依赖数组[],确保只在组件挂载时执行一次,完全对应类组件的componentDidMount:

useEffect(() => {
  const fetchClientToken = async () => {
    const res = await fetch('server.test/client_token');
    const clientToken = await res.json();
    setToken(clientToken);
  };
  fetchClientToken();
}, []);

2. 处理instance实例

用useRef保存Braintree的实例,useRef存储的变量不会触发组件重渲染,适合保存这种需要持久化的可变实例:

const instanceRef = useRef(null);

// 在DropIn的onInstance回调中赋值
<DropIn
  options={{ authorization: token }}
  onInstance={(instance) => {
    instanceRef.current = instance;
  }}
/>

// 在buy函数中调用实例方法
const buy = async () => {
  if (!instanceRef.current) return;
  const { nonce } = await instanceRef.current.requestPaymentMethod();
  await fetch(`server.test/purchase/${nonce}`);
};

3. 修正尝试代码中的其他错误

  • setToken(token)改为setToken(clientToken),否则无法更新正确的服务端返回token
  • DropIn的options写法错误,修正为options={{ authorization: token }}
  • 修复Button标签的语法错误(闭合标签、属性引号不匹配)
  • 普通函数createAccountOptions需直接调用{createAccountOptions()},而非作为组件<createAccountOptions/>(小写开头会被识别为HTML原生标签)

完整修正后的函数组件代码

import React, { useState, useEffect, useRef } from 'react';
import { Button } from '@mui/material';
import DropIn from 'braintree-web-drop-in-react';

const BraintreeDropInPaymentMethod = () => {
  const [token, setToken] = useState(null); // 初始值设为null,方便判断加载状态
  const [user, setUser] = useState({});
  const instanceRef = useRef(null);

  // 替代componentDidMount,获取clientToken
  useEffect(() => {
    const fetchClientToken = async () => {
      try {
        const res = await fetch('server.test/client_token');
        const clientToken = await res.json();
        setToken(clientToken);
      } catch (error) {
        console.error('获取Client Token失败:', error);
      }
    };
    fetchClientToken();
  }, []);

  const buy = async () => {
    if (!instanceRef.current) {
      console.error('Braintree实例未初始化');
      return;
    }
    try {
      const { nonce } = await instanceRef.current.requestPaymentMethod();
      await fetch(`server.test/purchase/${nonce}`);
      // 可添加支付成功后的逻辑,比如提示用户、跳转页面等
    } catch (error) {
      console.error('支付请求失败:', error);
    }
  };

  const handleCancelAccountCreation = () => {
    setUser({});
    const signInBtn = document.getElementById('signInBtn');
    if (signInBtn) signInBtn.hidden = false;
  };

  const renderContent = () => {
    if (!token) {
      return (
        <div>
          <h1>Loading...</h1>
        </div>
      );
    }
    return (
      <div>
        <DropIn
          options={{ authorization: token }}
          onInstance={(instance) => {
            instanceRef.current = instance;
          }}
        />
        <Button
          variant='contained'
          onClick={buy}
        >
          Create Account
        </Button>
        <Button
          variant='outlined'
          sx={{ marginLeft: 3 }}
          color='warning'
          onClick={handleCancelAccountCreation}
          href='/store-front'
        >
          Cancel
        </Button>
      </div>
    );
  };

  return <>{renderContent()}</>;
};

export default BraintreeDropInPaymentMethod;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 02:35:21