如何在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
相关产品推荐
相关产品推荐

