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

React组件中如何同时使用两个不同定时的setInterval?

问题描述

我有一个VerifyScreen页面,用户未验证邮箱时会被引导至该页面。需要实现两个定时功能:

  • 每5秒检查一次用户是否完成邮箱验证
  • 邮件重发按钮的倒计时,归零后用户可重新发送邮件

单独使用任意一个定时器时功能正常,但二者无法同时运行:点击重发按钮触发倒计时后,用户检查定时器会暂停,直到倒计时结束才恢复。后来我把用户检查的setInterval移到useEffect外部,两个定时器能同时运行了,但userTimer有时会在控制台触发两次,不确定是模拟器问题还是代码错误。

原逻辑代码:

const [emailTimer, setEmailTimer] = useState(0);
const [, setUser] = useAtom(userAtom);

const userTimer = setInterval(async () => {
  const refreshedUser = auth().currentUser;
  console.log('checked user');

  if (refreshedUser?.emailVerified) {
    clearInterval(userTimer);
    setUser(refreshedUser);
  } else {
    await auth().currentUser?.reload();
  }
}, 10000);

useEffect(() => {
  const resendEmailTimer = setInterval(() => {
    if (emailTimer <= 0) {
      clearInterval(resendEmailTimer);
    } else {
      console.log('second subtracted');
      setEmailTimer(emailTimer - 1);
    }
  }, 1000);

  return () => {
    clearInterval(userTimer);
    clearInterval(resendEmailTimer);
  };
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [emailTimer]);

邮件重发按钮组件代码:

<View style={{ paddingBottom: 10 }}>
  <PrimaryButton
    disabled={emailTimer > 0}
    onPress={async () => {
      setEmailTimer(60);
      await handleSendEmailConfirmation();
    }}>
    <CustomText
      text={emailTimer > 0 ? `${emailTimer}` : 'Resend email'}
      color={theme.colors.white}
    />
  </PrimaryButton>
</View>
<OutlinedButton
  onPress={async () => {
    await handleLogOut();
  }}>
  <CustomText text="Cancel" />
</OutlinedButton>

问题分析与修复

核心问题

  1. 用户检查定时器放在组件顶层,每次组件重渲染(比如emailTimer变化)都会创建新的定时器,导致多个定时器同时运行,出现重复打印的问题。
  2. 倒计时定时器依赖emailTimer,每次倒计时更新都会重建定时器,且清理函数会清除用户检查定时器,导致其被意外终止。

修复方案

  • 将用户检查定时器放到独立的useEffect中,仅在组件挂载时创建、卸载时清除,避免重复创建。
  • 倒计时定时器使用函数式更新setEmailTimer(prev => prev - 1),避免依赖emailTimer导致频繁重建定时器,同时保证拿到最新的倒计时数值。

修正后的完整代码
import { useState, useEffect } from 'react';
import { useAtom } from 'jotai';
import { auth } from '../your-auth-config'; // 替换为你的auth配置路径
import { userAtom } from '../your-atom-config'; // 替换为你的atom配置路径

const VerifyScreen = () => {
  const [emailTimer, setEmailTimer] = useState(0);
  const [, setUser] = useAtom(userAtom);

  // 仅在组件挂载时启动用户验证检查定时器
  useEffect(() => {
    const userCheckTimer = setInterval(async () => {
      const currentUser = auth().currentUser;
      console.log('checked user');

      if (!currentUser) return;

      // 先刷新用户信息,确保拿到最新状态
      await currentUser.reload();
      const refreshedUser = auth().currentUser;

      if (refreshedUser?.emailVerified) {
        clearInterval(userCheckTimer);
        setUser(refreshedUser);
      }
    }, 5000); // 改为需求的5秒检查间隔

    return () => clearInterval(userCheckTimer);
  }, [setUser]);

  // 重发邮件倒计时定时器
  useEffect(() => {
    if (emailTimer <= 0) return;

    const resendTimer = setInterval(() => {
      setEmailTimer(prev => {
        const newTime = prev - 1;
        // 倒计时归零时自动清除定时器
        if (newTime <= 0) clearInterval(resendTimer);
        return newTime;
      });
    }, 1000);

    return () => clearInterval(resendTimer);
  }, [emailTimer]);

  // 重发邮件逻辑
  const handleSendEmailConfirmation = async () => {
    const currentUser = auth().currentUser;
    if (currentUser) await currentUser.sendEmailVerification();
  };

  // 登出逻辑
  const handleLogOut = async () => {
    await auth().signOut();
    // 可在此添加跳转至登录页的逻辑
  };

  return (
    <View>
      {/* 邮件重发与取消按钮 */}
      <View style={{ paddingBottom: 10 }}>
        <PrimaryButton
          disabled={emailTimer > 0}
          onPress={async () => {
            setEmailTimer(60);
            await handleSendEmailConfirmation();
          }}>
          <CustomText
            text={emailTimer > 0 ? `${emailTimer}` : 'Resend email'}
            color={theme.colors.white}
          />
        </PrimaryButton>
      </View>
      <OutlinedButton onPress={handleLogOut}>
        <CustomText text="Cancel" />
      </OutlinedButton>
    </View>
  );
};

export default VerifyScreen;

关键修复点

  • 用户检查定时器与倒计时定时器完全独立,互不干扰,各自在专属的useEffect中管理生命周期。
  • 倒计时使用函数式更新解决闭包问题,确保每次更新都基于最新的倒计时数值。
  • 优化用户检查逻辑:先调用reload刷新用户信息,再获取最新状态,保证验证状态的准确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 11:46:08