使用EthersJS监听合约事件时多次触发的问题求助
问题分析与解决
核心错误点
重复创建Contract实例,旧实例监听未彻底清除
你的useEffect虽设置为空依赖,但组件每次挂载时都会新建一个ethers.Contract实例。每个实例都会独立绑定事件监听,而清理函数仅清除了当前实例的监听,旧实例的监听仍绑定在provider上,导致同一事件触发时多个监听同时执行。清理逻辑覆盖不全
直接调用removeAllListeners()仅针对当前实例生效,若provider是全局复用的,旧实例的监听仍会通过provider接收事件,无法被彻底清除。另外,若provider本身存在重新创建的情况,也会导致监听堆积。
修正后的代码示例
import { useRef, useEffect } from 'react'; import { ethers } from 'ethers'; function YourDAppComponent() { // 用useRef缓存Contract实例,避免重复创建 const contractRef = useRef(null); useEffect(() => { const contract = new ethers.Contract(contractAddress, Contract.abi, provider); contractRef.current = contract; // 单独定义事件处理函数,方便后续精准移除 const handleDeposit = (account, amount) => { toast({ title: 'Deposit Event', description: `Account : ${account} - amount ${ethers.utils.formatEther(amount)}`, status: 'success', duration: 5000, isClosable: true, }); }; contract.on("etherDeposited", handleDeposit); // 清理函数:精准移除对应事件的监听 return () => { if (contractRef.current) { contractRef.current.off("etherDeposited", handleDeposit); contractRef.current = null; } }; }, [provider]); // 必须将provider加入依赖数组,确保provider变化时重新绑定监听 // ... 组件其他业务逻辑 }
额外注意事项
- 保证provider稳定性:若
provider通过hooks获取,可用useRef缓存避免频繁重建,防止不必要的useEffect触发。 - 格式化事件返回值:事件返回的
amount是BigNumber类型,直接拼接会显示原始大数(如1e18),建议用ethers.utils.formatEther()转换为可读的ETH单位。 - 彻底清理监听:组件卸载时务必移除对应事件监听,避免内存泄漏和重复触发问题。
内容的提问来源于stack exchange,提问作者Ben BK
相关产品推荐
相关产品推荐

