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

如何在React useEffect中正确使用Solidity事件?解决重复订阅问题

解决React useEffect中Solidity事件监听器订阅与取消的问题

核心问题分析

你的代码中每次调用counterContract.events.Increment()或Decrement()都会生成全新的事件实例,订阅时用的是一个实例,清理时又创建了另一个新实例调用unsubscribe/removeAllListeners,自然无法取消之前的订阅。再加上React开发环境下的严格模式会刻意执行两次useEffect(先执行订阅+清理,再重新订阅),导致残留多个无效监听器。

正确解决方案

方案1:保存事件实例,复用实例执行取消操作

将订阅时的事件实例保存到变量中,清理时直接对同一个实例执行取消操作:

useEffect(() => {
  console.log('ADD');

  // 保存事件实例,避免每次调用生成新对象
  const incrementEvent = counterContract.events.Increment();
  const decrementEvent = counterContract.events.Decrement();

  // 绑定监听器
  incrementEvent.on('data', () => ReadContractValue());
  decrementEvent.on('data', () => ReadContractValue());

  return () => {
    console.log('REMOVE');
    // 对同一个实例执行取消订阅
    incrementEvent.unsubscribe();
    decrementEvent.unsubscribe();
  };
}, []);

方案2:直接使用订阅返回的取消函数(推荐,以Ethers.js为例)

如果你的合约库是Ethers.js,on方法会直接返回一个取消订阅的函数,直接保存并调用即可,无需管理事件实例:

useEffect(() => {
  console.log('ADD');

  // 订阅事件并保存取消函数
  const unsubscribeIncrement = counterContract.events.Increment().on('data', () => ReadContractValue());
  const unsubscribeDecrement = counterContract.events.Decrement().on('data', () => ReadContractValue());

  return () => {
    console.log('REMOVE');
    // 调用取消函数完成清理
    unsubscribeIncrement();
    unsubscribeDecrement();
  };
}, []);

额外说明

  • React严格模式下的两次ADD/REMOVE是开发环境专属的检测行为,用于排查未正确清理的副作用,生产环境只会执行一次订阅。只要清理函数正确,第二次订阅的监听器会覆盖无效的旧监听器,不会导致逻辑问题。
  • 若需要移除特定监听器而非全部,可使用removeListener方法,但需保存绑定的回调函数引用(避免匿名函数导致无法匹配):
    useEffect(() => {
      const handleEvent = () => ReadContractValue();
      const incrementEvent = counterContract.events.Increment();
      incrementEvent.on('data', handleEvent);
    
      return () => {
        incrementEvent.removeListener('data', handleEvent);
      };
    }, []);
    

内容的提问来源于stack exchange,提问作者Alex Csiszár

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 17:36:08