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

如何在React Native中实现可滑动设置的倒计时计时器?

React Native 倒计时计时器实现方案

核心思路是把时间状态从字符串改为秒数(便于倒计时计算),通过setInterval实现每秒递减,同时处理定时器的创建与清理,确保组件卸载时不会内存泄漏。以下是修改后的完整代码:

import { useNavigation } from '@react-navigation/core';
import React, { useEffect, useState, useRef } from 'react';
import { SafeAreaView, Text, Image, TouchableOpacity, StyleSheet, View } from 'react-native';
import Slider from '@react-native-community/slider';

import abacatezinImg from '../assets/abacatezinin.png';
import botaoStop from '../assets/botaoStop.png';
import botaoPlay from '../assets/botaoPlay.png';

// 辅助函数:将秒数格式化为 MM:SS
const formatTime = (seconds) => {
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
};

export function Timer(){
    const [totalSeconds, setTotalSeconds] = useState(30 * 60); // 初始30分钟,存为秒数
    const [remainingSeconds, setRemainingSeconds] = useState(totalSeconds);
    const [isSettingTime, setIsSettingTime] = useState(true); // 替换原isSecureEntry,语义更清晰
    const intervalRef = useRef(null); // 存定时器ID,用于清除
    const navigation = useNavigation<any>();
    
    function handleQuit(){
        // 清理定时器后再跳转
        if (intervalRef.current) clearInterval(intervalRef.current);
        navigation.navigate('Quit');
    }

    // 启动倒计时
    const startCountdown = () => {
        setIsSettingTime(false);
        // 每秒减1,直到剩余时间为0
        intervalRef.current = setInterval(() => {
            setRemainingSeconds(prev => {
                if (prev <= 1) {
                    clearInterval(intervalRef.current);
                    // 倒计时结束后自动切换回设置界面,可按需修改逻辑
                    setIsSettingTime(true);
                    return totalSeconds;
                }
                return prev - 1;
            });
        }, 1000);
    };

    // 停止倒计时并重置
    const stopCountdown = () => {
        if (intervalRef.current) clearInterval(intervalRef.current);
        setRemainingSeconds(totalSeconds);
        setIsSettingTime(true);
    };

    // 组件卸载时清理定时器
    useEffect(() => {
        return () => {
            if (intervalRef.current) clearInterval(intervalRef.current);
        };
    }, []);

    if(isSettingTime){
        return(
            <SafeAreaView style={styles.container}>
                <Text style={styles.texto}>{formatTime(totalSeconds)}</Text>
            <View style={styles.container2}>
                
                <Slider 
                style={styles.slider}
                minimumValue={0}
                maximumValue={60}
                minimumTrackTintColor='#7E9F70'
                maximumTrackTintColor='#A7C99A'
                thumbImage={abacatezinImg}
                value={totalSeconds / 60} // 滑块值对应分钟数
                onValueChange={value => {
                    const mins = parseInt(value);
                    setTotalSeconds(mins * 60);
                    setRemainingSeconds(mins * 60);
                }}
                step={5}
                />
                <TouchableOpacity onPress={startCountdown}>
                        <Image source={botaoPlay} style={styles.bPlay}/>
                </TouchableOpacity>
            </View>

            </SafeAreaView>
        );
    } else {
        return(
            <SafeAreaView style={styles.container}>
                <Text style={styles.texto}>{formatTime(remainingSeconds)}</Text>
            <View style={styles.container3}>
                <TouchableOpacity onPress={stopCountdown}>
                        <Image source={botaoStop} style={styles.bStop}/>
                </TouchableOpacity>
            </View>
    
            </SafeAreaView>
        );
    }
}

const styles = StyleSheet.create({
    container:{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'flex-start',
    },
    container2:{
        backgroundColor: '#E4F0E2',
        alignItems: 'center',
        justifyContent: 'space-between',
        flexDirection: 'row',
        borderRadius: 100,
        width: 327,
        height: 66,
        paddingHorizontal: 19,
        marginTop: 20,
    },
    container3:{
        alignItems: 'center',
        justifyContent:'space-between',
        marginTop: 20,
    },
    slider:{
        width: 209,
        height: 50,
        transform: [{scaleY: 4}]
    },
    bPlay:{
        width: 50,
        height: 50,
    },
    bStop:{
        width: 66,
        height: 66,
    },
    texto:{
        fontWeight: '300',
        fontSize: 58,
        lineHeight: 87,
        letterSpacing: 0.03,
        color: '#1B3810',
    },
})

关键改动说明:

  • 时间状态存储:用totalSeconds(用户设置的总时间,秒数)和remainingSeconds(倒计时剩余时间,秒数)替代原字符串类型的range,避免字符串解析的麻烦。
  • 定时器管理:用useRef存储定时器ID,确保组件重新渲染时能正确引用同一个定时器,并且在组件卸载、停止倒计时时及时清除,防止内存泄漏。
  • 时间格式化:新增formatTime辅助函数,将秒数统一转为MM:SS格式的字符串,确保显示符合要求。
  • 状态语义优化:把isSecureEntry改为isSettingTime,更贴合功能逻辑,便于理解。
  • 滑块逻辑调整:滑块值对应分钟数,转换为秒数后更新状态,确保时间计算准确。
  • 倒计时结束处理:当剩余时间归0时,自动清除定时器并切换回时间设置界面,可根据需求修改这部分逻辑(比如弹窗提示)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 21:25:16