如何将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),否则无法更新正确的服务端返回tokenDropIn的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
相关产品推荐
相关产品推荐

