一、验证码倒计时器 核心需求 & 核心特性

1. 核心技术点拆解(零基础易懂,无门槛)

实现本倒计时器仅需掌握 3 个 React Native 入门级核心知识点,吃透这 3 个知识点,不仅能实现倒计时,更能举一反三实现所有定时器相关功能:

  1. useState 状态管理:定义「倒计时剩余秒数、按钮是否禁用」两个核心状态,通过修改状态值实现按钮文字、禁用状态的动态更新;
  2. useEffect 副作用钩子:核心实现「定时器的创建、倒计时逻辑执行、定时器的清除销毁」,是整个倒计时器的核心逻辑载体;
  3. TouchableOpacity 按钮组件:鸿蒙端最常用的可点击组件,支持disabled禁用属性,完美适配倒计时的禁用 / 启用状态切换。

二、核心原理:零基础拆解 验证码倒计时完整逻辑

很多新手觉得倒计时器有难度,本质是没理清「状态 + 定时器」的联动逻辑,其实整个倒计时的核心逻辑只有5 步,极简易懂,这也是本文所有实战代码的核心底层逻辑,记牢之后,无论怎么拓展功能,都不会偏离核心:

步骤 1:定义核心状态

  • 定义 countDown 状态:存储倒计时剩余秒数,初始值为0(默认无倒计时);
  • 定义 isDisabled 状态:存储按钮是否禁用,初始值为false(默认可点击)。

步骤 2:点击「获取验证码」触发倒计时

  • 点击按钮时,手动将 countDown 设置为目标倒计时秒数(如 60),将 isDisabled 设置为true(立即禁用按钮);
  • 触发倒计时的同时,可调用「发送验证码的接口」,实现点击获取验证码的业务逻辑。

步骤 3:开启定时器执行倒计时逻辑

  • 通过 useEffect 监听 countDown 状态的变化,当 countDown > 0 时,开启一个「每秒执行一次」的定时器;
  • 定时器内部逻辑:每执行一次,将 countDown 减 1,实现秒数的实时递减。

 步骤 4:倒计时结束自动恢复状态

  • 当定时器执行到 countDown === 0 时,自动停止定时器;
  • 同时将 isDisabled 恢复为false,按钮重新变为可点击状态,文字切换为「重新发送验证码」。

步骤 5:页面卸载清除定时器(重中之重)

  • 通过 useEffect 的「返回清理函数」,在页面卸载 / 组件销毁时,强制清除定时器;
  • 核心作用:防止页面销毁后定时器还在后台运行,导致内存泄漏、应用卡顿,这是新手必踩的坑,也是生产级代码的必备规范。

三、实战核心一:标准版通用验证码倒计时器

import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

const SmsCodeCountDown = () => {
  const [countDown, setCountDown] = useState(0); // 倒计时剩余秒数,初始0=无倒计时
  const [isDisabled, setIsDisabled] = useState(false); // 按钮是否禁用,初始false=可点击
  const countDownTime = 60; // 通用验证码倒计时时长,固定60秒,可按需修改

useEffect(() => {
  let timer: NodeJS.Timeout | null = null;

  if (countDown > 0) {
    timer = setInterval(() => {
      setCountDown(prev => prev - 1); // 秒数递减
    }, 1000);
  } else {
    // 倒计时结束,恢复按钮可点击状态
    setIsDisabled(false);
  }

  return () => {
    if (timer) clearInterval(timer);
  };
}, [countDown]); 

  const handleGetCode = () => {
    // 按钮禁用时,禁止执行任何逻辑(双重防点击)
    if (isDisabled) return;

    //  步骤1:禁用按钮,防止重复点击
    setIsDisabled(true);
    //  步骤2:启动倒计时,设置为60秒
    setCountDown(countDownTime);

    // 步骤3:这里写【调用发送验证码的接口】逻辑
    console.log('✅ 验证码发送成功,开始倒计时!');
  };

  const getBtnText = () => {
    if (countDown > 0) {
      return `${countDown}秒后重新发送`;
    } else {
      return '获取验证码';
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>鸿蒙端通用验证码倒计时演示</Text>
      {/* 验证码输入框占位(可直接对接项目中的输入框) */}
      <View style={styles.inputWrap}>
        <Text style={styles.inputLabel}>手机号:</Text>
        <Text style={styles.inputText}>13800138000</Text>
      </View>

      {/* 🔥 核心:验证码倒计时按钮 */}
      <TouchableOpacity
        style={[styles.codeBtn, isDisabled ? styles.codeBtnDisabled : styles.codeBtnActive]}
        onPress={handleGetCode}
        disabled={isDisabled} // 绑定禁用状态
        activeOpacity={0.7} // 鸿蒙端点击透明度反馈,提升交互体验
      >
        <Text style={[styles.btnText, isDisabled ? styles.btnTextDisabled : styles.btnTextActive]}>
          {getBtnText()}
        </Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f7f8fa',
    paddingHorizontal: 20,
  },
  title: {
    fontSize: 20,
    color: '#1a1a1a',
    fontWeight: '600',
    marginBottom: 30,
  },
  inputWrap: {
    width: '90%',
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#ffffff',
    padding: 12,
    borderRadius: 8,
    marginBottom: 20,
    borderWidth: 1,
    borderColor: '#e0e0e0',
  },
  inputLabel: {
    fontSize: 16,
    color: '#333333',
  },
  inputText: {
    fontSize: 16,
    color: '#666666',
    flex: 1,
    marginLeft: 10,
  },
  // 验证码按钮基础样式
  codeBtn: {
    width: '90%',
    height: 50,
    borderRadius: 8,
    justifyContent: 'center',
    alignItems: 'center',
  },
  codeBtnActive: {
    backgroundColor: '#007DFF',
  },
  codeBtnDisabled: {
    backgroundColor: '#99CCFF',
  },
  btnText: {
    fontSize: 16,
    fontWeight: '500',
  },
  btnTextActive: {
    color: '#ffffff',
  },
  btnTextDisabled: {
    color: '#ffffff',
    opacity: 0.9,
  },
});

export default SmsCodeCountDown;

3.3 鸿蒙端运行效果

四、实战核心二: 可高度自定义的通用倒计时器

import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

interface CountDownConfig {
  countDownTime: number; // 倒计时时长
  btnTextNormal: string; 
  btnTextCounting: (time: number) => string; 
  activeColor: string; 
  disabledColor: string; 
}

const CustomCodeCountDown = () => {
  const config: CountDownConfig = {
    countDownTime: 60,
    btnTextNormal: '获取验证码',
    btnTextCounting: (time) => `${time}秒 · 重新发送`,
    activeColor: '#00C853',
    disabledColor: '#CCE5FF',
  };

  const [countDown, setCountDown] = useState<number>(0);
  const [isDisabled, setIsDisabled] = useState<boolean>(false);

  useEffect(() => {
    let timer: NodeJS.Timeout | null = null;

    if (countDown > 0) {
      timer = setInterval(() => {
        setCountDown(prev => prev - 1); // 函数式更新,避免状态偏差
      }, 1000);
    } else {
      setIsDisabled(false);
    }

    return () => {
      if (timer) clearInterval(timer);
    };
  }, [countDown]);

  // 点击事件
  const handleGetCode = () => {
    if (isDisabled) return;
    setIsDisabled(true);
    setCountDown(config.countDownTime);
    console.log('✅ 自定义验证码发送成功!');
  };

  // 动态文案
  const getBtnText = () => {
    return countDown > 0 ? config.btnTextCounting(countDown) : config.btnTextNormal;
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>鸿蒙端自定义配置倒计时演示</Text>
      <TouchableOpacity
        style={[
          styles.codeBtn,
          { backgroundColor: isDisabled ? config.disabledColor : config.activeColor }
        ]}
        onPress={handleGetCode}
        disabled={isDisabled}
        activeOpacity={0.7}
      >
        <Text style={styles.btnText}>{getBtnText()}</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f7f8fa',
    paddingHorizontal: 20,
  },
  title: {
    fontSize: 20,
    color: '#1a1a1a',
    fontWeight: '600',
    marginBottom: 30,
  },
  codeBtn: {
    width: '90%',
    height: 50,
    borderRadius: 8,
    justifyContent: 'center',
    alignItems: 'center',
  },
  btnText: {
    fontSize: 16,
    fontWeight: '500',
    color: '#ffffff',
  },
});

export default CustomCodeCountDown;

五、鸿蒙端开发避坑指南

这 4 个坑点是所有 RN 开发者在实现倒计时器时高频踩坑的点,也是面试常问的知识点,全部规避后,你的代码就是「无 BUG、高性能、生产级」的优质代码,零基础也能写出大厂规范的逻辑:

坑点 1:页面卸载后定时器未清除,导致内存泄漏

解决方案:在useEffect中返回清理函数,调用clearInterval(timer)清除定时器,这是本文所有代码都已实现的核心规范,必须遵守!

坑点 2:点击按钮时未做禁用判断,多次点击触发多个定时器,倒计时混乱

解决方案:按钮绑定disabled={isDisabled}属性 + 点击方法内加if(isDisabled) return双重判断,彻底杜绝重复点击,从根源解决定时器混乱问题;

坑点 3:直接修改 state 值,导致倒计时秒数不准

解决方案:修改countDown时,使用「函数式更新」setCountDown(prev => prev - 1),而非直接setCountDown(countDown - 1),确保每次取到的都是最新的秒数,避免异步更新导致的倒计时偏差;

坑点 4:硬编码倒计时时长 / 文案,组件复用性差

解决方案:进阶版中使用配置项统一管理所有可自定义的参数,只需修改配置项即可适配不同场景,无需改动核心逻辑,提升代码复用性。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐