React/NextJS环境下调用Ethereum API发起交易重复执行问题
问题分析与解决方案
核心原因
在React/NextJS环境中使用原生addEventListener绑定点击事件,容易因为组件重复渲染(比如开发环境的StrictMode、状态更新触发重渲染、SSR/CSR双重渲染)导致同一个按钮被多次绑定点击事件处理函数,最终点击一次就触发两次交易请求。
解决方案
方案一:使用React内置事件绑定(推荐)
React会自动管理事件绑定的生命周期,避免重复绑定问题,代码更符合React开发规范:
import { useState } from 'react'; export default function TransferButton() { const handleTransfer = async () => { try { // 调用MetaMask的eth_sendTransaction接口 const txHash = await ethereum.request({ method: 'eth_sendTransaction', params: [ { from: '你的发起钱包地址', to: '目标钱包地址', value: '转换为wei的十六进制数值' // 示例:0xDE0B6B3A7640000 对应1ETH } ] }); console.log('交易哈希:', txHash); } catch (error) { console.error('交易失败:', error); } }; return ( <button onClick={handleTransfer}>transfer</button> ); }
方案二:原生DOM绑定+清理监听(兼容场景)
如果必须使用原生DOM操作,需通过useEffect确保事件监听只绑定一次,并在组件卸载时清理:
import { useEffect } from 'react'; export default function TransferButton() { useEffect(() => { const transferBtn = document.querySelector('#transfer'); const handleClick = async () => { try { const txHash = await ethereum.request({ method: 'eth_sendTransaction', params: [ { from: '你的发起钱包地址', to: '目标钱包地址', value: '转换为wei的十六进制数值' } ] }); console.log('交易哈希:', txHash); } catch (error) { console.error('交易失败:', error); } }; // 仅在按钮存在时绑定事件 transferBtn?.addEventListener('click', handleClick); // 组件卸载时移除监听,避免内存泄漏和重复绑定 return () => { transferBtn?.removeEventListener('click', handleClick); }; }, []); // 空依赖数组确保绑定逻辑只执行一次 return ( <button id='transfer'>transfer</button> ); }
额外注意事项
- 开发环境下React的
StrictMode会强制组件渲染两次,这是导致原生事件重复绑定的常见诱因,使用上述两种方案可规避该问题。 - 调用
ethereum.request前需确保MetaMask已连接,可通过ethereum.isConnected()或监听connect事件做前置校验。
内容的提问来源于stack exchange,提问作者sirblender
相关产品推荐
相关产品推荐

