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

React中邮件发送函数重复执行及console.log多次输出问题求助

解决邮件重复发送与console.log重复输出问题

我正在开发一个根据过期日期发送邮件的函数,测试时功能正常,但邮件会重复发送,且console.log会多次输出。请问该如何解决?

**注意:**我知道当前函数会在未到过期日期时发送邮件,我只想修复console.log重复输出的问题。

当前代码片段

if (product?.expiry) {
      var arr = []
      var today = new Date()
      var expiry = new Date(product?.expiry)
      var week = new Date(expiry.setDate(expiry.getDate() - 7))

      var template = {
        message: product.name + " is reaching expiry date",
        footer: "Expiry Date: " + week.toDateString()
      }

     const interval =  setInterval(() => {
      if (arr.length > 0) {
        clearInterval(interval)
        console.log(arr)
       }
        if (today !== expiry) {
          // actual emailing
            emailjs.send(SERVICE_API, TEMPLATE_API, template, PUBLIC_API)
            .then(function(response) {
              console.log('SUCCESS!', response.status, response.text);
            }, function(error) {
              console.log('FAILED...', error)
            });
            arr.push('found')
          }
     }, 1000)
 }

函数剩余部分

const expiryEmail = () => {
        if (today !== week) {
          emailjs.send(SERVICE_API, TEMPLATE_API, template, PUBLIC_API)
          .then(function(response) {
            console.log('SUCCESS!', response.status, response.text);
          }, function(error) {
            console.log('FAILED...', error)
          });
        }
      }

      setTimeout(() => {
        
      })
      useEffect(() => {
          if (!product) {
            return;
          }
          //problem is that the condition returns null first time around
          expiryEmail()
      },[])

解决方案

核心问题分析

  • setInterval 每秒执行一次,清除定时器后未终止后续代码,导致仍会触发邮件发送和日志输出
  • useEffect 未处理组件卸载时的定时器清理,可能导致内存泄漏和重复执行
  • Date对象直接用!==比较是引用比较,逻辑错误且可能触发不必要的执行
  • 无状态标记,无法控制expiryEmail只执行一次

修改步骤

1. 添加发送状态标记,避免重复执行

用useRef记录邮件是否已发送,确保expiryEmail只触发一次:

import { useEffect, useRef } from 'react';

// ...其他代码

const expiryEmail = () => {
  // 转换日期为当天0点的时间戳,避免时分秒干扰比较
  const todayTimestamp = new Date().setHours(0, 0, 0, 0);
  const weekTimestamp = new Date(product.expiry).setDate(new Date(product.expiry).getDate() - 7).setHours(0, 0, 0, 0);
  
  if (todayTimestamp !== weekTimestamp) {
    emailjs.send(SERVICE_API, TEMPLATE_API, template, PUBLIC_API)
      .then(response => console.log('SUCCESS!', response.status, response.text))
      .catch(error => console.log('FAILED...', error));
  }
}

useEffect(() => {
  if (!product) return;

  const hasSent = useRef(false);
  if (!hasSent.current) {
    expiryEmail();
    hasSent.current = true;
  }

  // ...定时器逻辑(如果需要保留的话)
}, [product]);
2. 修复定时器逻辑,清除后终止执行

如果必须保留setInterval,修改后确保清除定时器后不再执行后续代码:

const interval = setInterval(() => {
  if (arr.length > 0) {
    clearInterval(interval);
    console.log(arr);
    return; // 清除后直接返回,避免触发邮件发送
  }
  
  // 改用时间戳比较日期
  const todayTimestamp = today.setHours(0, 0, 0, 0);
  const expiryTimestamp = expiry.setHours(0, 0, 0, 0);
  
  if (todayTimestamp !== expiryTimestamp) {
    emailjs.send(SERVICE_API, TEMPLATE_API, template, PUBLIC_API)
      .then(res => console.log('SUCCESS!', res.status, res.text))
      .catch(err => console.log('FAILED...', err));
    arr.push('found');
  }
}, 1000);
3. 清理定时器,避免内存泄漏

在useEffect的返回函数中清除定时器,防止组件卸载后定时器继续运行:

useEffect(() => {
  let interval;
  if (product?.expiry) {
    // ...定时器初始化逻辑
  }

  // 组件卸载时清除定时器
  return () => {
    if (interval) clearInterval(interval);
  };
}, [product]);

内容的提问来源于stack exchange,提问作者ryand123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 14:06:15