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

React日期对比抽奖页面问题:触发日期时重复渲染及函数多次调用

解决React抽奖网站日期匹配时重复渲染问题

问题出在这两个地方:

  1. 直接在组件渲染阶段执行日期判断和setWinner,一旦时间匹配,每次渲染都会触发状态更新,导致React无限重复渲染,RandomNumber被反复调用。
  2. 用toLocaleDateString/toLocaleTimeString做日期对比,容易受系统地区、格式差异影响;之前尝试的毫秒对比可能因为日期字符串解析错误(比如"12/12/2022"的解析结果受时区干扰),导致匹配逻辑被跳过。

下面是修复后的完整代码和关键改动说明:

修复后的Datetime组件

import { getRandomWinner } from "components/RandomNumber";
import { useEffect, useState, useRef } from "react";
import "./styles.css";

const Datetime = () => {
  const [winner, setWinner] = useState("");
  const [dateState, setDateState] = useState(new Date());
  const timerRef = useRef<NodeJS.Timeout | null>(null);

  // 用ISO格式定义目标日期,避免时区解析问题,转换成时间戳方便对比
  const targetDate = new Date("2022-12-12T10:33:00");
  const targetTimestamp = targetDate.getTime();

  useEffect(() => {
    // 组件挂载时创建一次定时器,存到ref里方便后续清除
    timerRef.current = setInterval(() => setDateState(new Date()), 1000);
    return () => {
      if (timerRef.current) clearInterval(timerRef.current);
    };
  }, []);

  useEffect(() => {
    // 已经生成获奖者就不再执行逻辑
    if (winner) return;

    const currentTimestamp = dateState.getTime();
    // 允许±1秒误差,覆盖定时器精度和系统时间的微小偏差
    if (Math.abs(currentTimestamp - targetTimestamp) <= 1000) {
      const newWinner = getRandomWinner();
      setWinner(newWinner);
      // 生成获奖者后可以停止定时器,节省资源
      if (timerRef.current) clearInterval(timerRef.current);
    }
  }, [dateState, winner, targetTimestamp]);

  const currentDate = dateState.toLocaleDateString("pt-BR", {
    day: "numeric",
    month: "long",
    year: "numeric",
  });

  const currentTime = dateState.toLocaleString("pt-BR", {
    hour: "numeric",
    minute: "numeric",
    second: "numeric",
    hour12: true,
  });

  return (
    <>
      <p className="date-custom">{currentDate}</p>
      <p className="time-custom">{currentTime}</p>
      {winner && <div className="winner">获奖者:{winner}</div>}
    </>
  );
};

export default Datetime;

优化后的RandomNumber工具函数(改成纯函数,避免组件冗余)

export const getRandomWinner = () => {
  const items = [
    1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 19, 20, 21, 24, 26, 27, 28, 29, 30,
    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 49, 50, 56,
  ];
  const item = items[Math.floor(Math.random() * items.length)];

  const numberToName = (item: number): string => {
    switch(item) {
      case 1: return `Philipe Jensen ${item}`;
      case 2: return `Jão ${item}`;
      case 3: return `Xepa ${item}`;
      case 5: case 7: case 10: case 11: case 15: case 19: case 26: case 29: case 30: case 50:
        return `Leleo ${item}`;
      case 8: return `Felipe ${item}`;
      case 9: case 21: return `Pimpão ${item}`;
      case 12: return `Monteiro ${item}`;
      case 13: return `May ${item}`;
      case 20: return `Bugs ${item}`;
      case 24: case 56: return `Vev ${item}`;
      case 27: return `Tic ${item}`;
      case 28: return `Helo Ams ${item}`;
      case 31: case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40:
      case 41: case 42: case 43: case 44: case 45: case 46:
        return `Izumi ${item}`;
      case 49: return `Geobanna ${item}`;
      default: return '';
    }
  };

  return numberToName(item);
};

关键改动说明

  • 精准日期对比:用ISO格式创建目标日期,转换成时间戳后对比,还加入±1秒误差范围,解决时区解析问题和定时器精度不足导致的匹配遗漏。
  • 避免无限渲染:把获奖者生成逻辑移到useEffect中,依赖dateState和winner,只有时间变化且未生成获奖者时才执行,杜绝渲染阶段触发状态更新的循环。
  • 优化定时器:用useRef存储定时器ID,组件挂载时只创建一次,生成获奖者后可直接清除,节省资源。
  • 简化抽奖逻辑:把RandomNumber从React组件改成纯工具函数,避免组件渲染的额外开销,调用更直接。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 18:20:31