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

使用EthersJS监听合约事件时多次触发的问题求助

问题分析与解决

核心错误点

  1. 重复创建Contract实例,旧实例监听未彻底清除
    你的useEffect虽设置为空依赖,但组件每次挂载时都会新建一个ethers.Contract实例。每个实例都会独立绑定事件监听,而清理函数仅清除了当前实例的监听,旧实例的监听仍绑定在provider上,导致同一事件触发时多个监听同时执行。

  2. 清理逻辑覆盖不全
    直接调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 06:41:33