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

React+Tailwind倒计时应用:实现用户输入后点击启动倒计时

解决倒计时应用的用户输入启动问题

原代码核心问题

  • 组件挂载后立即启动倒计时(第一个useEffect空依赖),未等待用户提交操作
  • 同一个ref同时绑定输入框与存储定时器,导致引用冲突
  • 输入值未转为数字,会引发字符串减法逻辑错误
  • 第二个useEffect无依赖项,频繁触发不必要的逻辑判断

修改后的完整代码

CountDown.js

import React, { useState, useEffect, useRef } from 'react'

const CountDown = () => {
  // 存储用户输入的原始值
  const [inputValue, setInputValue] = useState('');
  // 倒计时当前数值
  const [count, setCount] = useState(null);
  // 控制倒计时是否处于运行状态
  const [isRunning, setIsRunning] = useState(false);
  // 单独存储定时器引用,避免与输入框冲突
  const timerRef = useRef(null);

  // 处理输入框内容变化
  const handleChange = e => {
    setInputValue(e.target.value);
  }

  const handleSubmit = () => {
    const num = parseInt(inputValue, 10);
    // 验证输入是否为有效正整数
    if (isNaN(num) || num <= 0) {
      alert("请输入一个大于0的有效数字!");
      return;
    }
    // 清除之前可能存在的定时器
    if (timerRef.current) {
      clearInterval(timerRef.current);
    }
    // 设置初始倒计时值并启动倒计时
    setCount(num);
    setIsRunning(true);
  }

  // 倒计时核心逻辑,仅在运行状态或数值变化时触发
  useEffect(() => {
    if (isRunning && count !== null && count > 0) {
      timerRef.current = setInterval(() => {
        setCount(prev => {
          if (prev <= 1) {
            setIsRunning(false);
            alert("END!");
            return 0;
          }
          return prev - 1;
        });
      }, 1000);
    }

    // 清理函数:组件卸载或倒计时停止时清除定时器
    return () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
      }
    };
  }, [isRunning, count]);

  return (
    <div className='h-[100vh] w-full flex flex-col items-center justify-center'>
      <div className="border border-black p-12 items-center justify-center">
        <div className="flex justify-center">
          <p className='text-3xl font-bold'>Countdown App</p>
        </div>
        <div className="flex justify-center mt-10">
          <p className='font-semibold text-2xl'>{count !== null ? count : "请输入数字并提交"}</p>
        </div>
        <div className="flex mt-10 mb-4 items-center justify-center">
          <label htmlFor="cntDown" className='text-xl'>Enter a Number: </label>
          <input 
            className='border border-black ml-2 p-2 rounded-lg text-xl' 
            type="number" 
            name="cntDown" 
            id="cntDown" 
            value={inputValue}
            onChange={handleChange}
            disabled={isRunning} // 倒计时运行时禁用输入
          />
        </div>
        <div className="flex justify-center">
          <button 
            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" 
            onClick={handleSubmit}
            disabled={isRunning} // 倒计时运行时禁用提交按钮
          >
            {isRunning ? "倒计时中..." : "Submit"}
          </button>
        </div>
      </div>
    </div>
  )
}

export default CountDown

关键改动说明

  • 拆分状态:用inputValue存储用户输入,count存储当前倒计时值,isRunning控制运行状态,避免状态混乱
  • 独立定时器引用:不再与输入框绑定,解决引用冲突问题
  • 输入验证:提交时将输入转为整数,确保输入为有效正整数
  • 受控交互:倒计时运行时禁用输入框和提交按钮,防止重复操作
  • 优化副作用:useEffect仅在必要状态变化时执行,清理函数确保定时器彻底清除

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 18:22:38