I am in the early stages of learning react native along with TypeScript
and react-navigation. I am trying to implement a countdown timer app with editable time and labels. I was able to get most of the behaviors I'm wanting to work correctly but the last thing I'm stuck on is I want the timer to reset to the input value when the go button is pressed. other comments on the capabilities of the timer are in the comments of app.tsx
.
Along with assistance with this functionality, any input on better ways of doing this or more appropriate standards I should have in my code would be appreciated.
link to the snack if that works: https://snack.expo.io/@jpb159/two-tap-timer
app.tsx
// In App.js in a new project// React Navigation v5, Hooks, Interval and Typescript Practice// Timer app counting down in secconds until the time reaches 0// - Countdown should continue until either time reaches 0 or the 'go' button is pressed.// - Should reset the 'time' to the entered 'time value' every time the 'go' button is pushed then continue counting down to 0.// - No reset should happen when tabs are selected using bottomTabNav. // - Initial timer screen values are id= -1, timerName= "initial name", duration= 0, time= 0.import * as React from 'react';import { Button } from 'react-native';import AppTabs from './src/Navigation'function App() { return (<AppTabs /> );}export default App;
Navigation.tsx
import React, { useState } from 'react';import { NavigationContainer, RouteProp } from '@react-navigation/native';import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack';import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';import HomeScreen from '../Screens/HomeScreen';import EditScreen from '../Screens/EditScreen';import TimerScreen, { TimerProps } from '../Screens/TimerScreen';type HomeStackParamList = { HomeScreen: undefined; EditScreen: undefined;};type TimerStackParamList = { TimerScreen: { id:number|undefined, timerName: string; duration: number } | undefined;};type TimerScreenRouteProp = RouteProp<TimerStackParamList, 'Timer Screen'>;type TimerScreenNavigationProp = StackNavigationProp< TimerStackParamList,'Timer Screen'>;export type NavProps = { navigation: TimerScreenNavigationProp, route: TimerScreenRouteProp,};const HomeStack = createStackNavigator<HomeStackParamList>();const TimerStack = createStackNavigator<TimerStackParamList>();function HomeTab({ route } : NavProps) { return (<HomeStack.Navigator><HomeStack.Screen name="Home Screen" component={HomeScreen} initialParams={{ timerName: "timer 1", }} /><HomeStack.Screen name="Edit Screen" component={EditScreen} /></HomeStack.Navigator> );}function TimerTab({ route } : NavProps) { const props = route.params.params ? route.params.params : {id:".params.params undefined",timerName:".params.params undefined", duration:".params.params undefined"} console.log("--TIMER TAB-\n"+"\t-id: "+props.id+"\t-timerName: "+props.timerName+"\t-duration: "+props.duration ); return (<TimerStack.Navigator><TimerStack.Screen name="Timer Screen" initialParams={{...props, id:-1}} component={TimerScreen} /></TimerStack.Navigator> );}type TabParamList = { Home: undefined; Timer: | { screen: 'Timer Screen'; params: { timerName: string; duration: number }; } | undefined;};const Tab = createBottomTabNavigator<TabParamList>();const AppTabs = () => { return (<NavigationContainer><Tab.Navigator initialRouteName="Home"><Tab.Screen name="Home Tab" component={HomeTab} /><Tab.Screen name="Timer Tab" component={TimerTab} initialParams={{ params:{ id: -1, timerName: "initial name", duration: 0, stop: true } }} /></Tab.Navigator></NavigationContainer> );};export default AppTabs;
Home Screen where the timer name and time are editable and where the go button navigates to the timer screen along with params
import React, { useState } from 'react';import { View, Text, Button, TextInput, Platform } from 'react-native';import { NavProps } from '../src/Navigation';import DateTimePicker from '@react-native-community/datetimepicker';function HomeScreen({ route, navigation }: NavProps) { const [text, onChangeText] = React.useState<string>(); const [val, onChangeVal] = React.useState<string>(); const [date, setDate] = useState<any>(new Date(1598051730000)); const [mode, setMode] = useState<any>('date'); const [show, setShow] = useState<boolean>(false); const onChange = (event: any, selectedDate: any) => { const currentDate = selectedDate || date; setShow(Platform.OS === 'ios'); setDate(currentDate); }; const showMode = (currentMode: string) => { setShow(true); setMode(currentMode); }; const showDatepicker = () => { showMode('date'); }; const showTimepicker = () => { showMode('time'); }; return (<View style={{ flex: 1, alignItems: 'center', justifyContent: 'space-evenly' }}><Text>Home Screen</Text><View style={{ flexDirection: 'column', width: '90%', padding: '1%', borderWidth: 10, }}><View style={{ flexDirection: 'row', width: '90%', alignItems: 'center', justifyContent: 'space-evenly', }}><Text> Label: </Text><TextInput placeholder="'Timer Name...'" value={text} onChangeText={onChangeText} /></View><View style={{ flexDirection: 'row', width: '90%', alignItems: 'center', justifyContent: 'space-evenly', }}><Button onPress={showTimepicker} title="Show time picker!" /> {show && (<DateTimePicker testID="dateTimePicker" value={date} mode={mode} is24Hour={true} display="default" onChange={onChange} /> )}<Button title="Go" onPress={() => { console.log("HOME SCREEN-\n"+"\t-id: "+1+"\t-timerName: "+text+"\t-duration: "+val ); navigation.navigate('Timer Tab', { screen: 'Timer Screen', params:{ id: 1, timerName: text, duration: Number(val), stop: false } }); }} /></View><View><Text> time value: </Text><TextInput placeholder="'enter time value...'" keyboardType="number-pad" value={val} onChangeText={onChangeVal} /></View></View>{/*<Text> other buttons:</Text><Button title="Timer Screen" onPress={() => { console.log('timer duration: '+ route.params.duration), navigation.navigate('Timer Tab', {screen:'Timer Screen', params:{ id:0 } } ); }} /><Button title="Edit Screen" onPress={() => { navigation.navigate('Edit Screen'); }} />*/} </View> );}export default HomeScreen;
Timer Screen where the countdown is displayed and should run until 0 (then console.logs 'timer done') or the 'go' button is pressed again resetting the time left
import React, { useEffect, useState } from 'react';import { View, Text, Button } from 'react-native';import { NavProps } from '../src/Navigation';export type TimerProps={ isPaused: boolean,}function TimerScreen({ route, navigation }: NavProps, props: TimerProps) { const { id, timerName, duration, stop } = route.params; const [initTime, setInitTime] = useState<number>(duration); const [time, setTime] = useState<number>(duration); const [isStopped, setIsStopped] = useState<boolean>(stop); console.log("----TIMER SCREEN-\n"+"\t-id: "+id+"\t-timerName: "+timerName+"\t-duration: "+duration+"\t-initTime: "+initTime+"\t-stop: "+isStopped ); useEffect(() => { if(initTime !== duration){ console.log("***duration changed!!!"); setTime(duration) setInitTime(duration) } console.log('time:'+time); const timerId = setInterval(() => { // console.log('time:'+time); if(!props.isPaused){ setTime(time-1); } else{console.log('timer paused: '+props.isPaused)} }, 1000) if(time<=0||time === null||time === undefined){ console.log('Timer Done timerId:'+timerId); setIsStopped(true); if(timerId) { // setInitTime(0); clearInterval(timerId); } return; } // const unsubscribe = navigation.addListener('focus', () => { // console.log('focussed'); // setTime(route.params.duration); // }); return () => clearInterval(timerId); }, [time, duration, initTime, isStopped, props.isPaused,]) return (<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}><Text>Timer Screen {"id:"+id}</Text><Text>timerName: {JSON.stringify(timerName)}</Text><Text>duration: {JSON.stringify(duration)}</Text><Text>time: {time}</Text><View></View><Button title="Timer Screen again" onPress={() => { navigation.push('Timer Screen', { id: Math.floor(Math.random() * 100), }); }} /><Button title="Go to Home" onPress={() => navigation.navigate('Home Tab', { screen: 'Home Screen' }) } /><Button title="Go back" onPress={() => navigation.goBack()} /><Button title="Go back to first screen in stack" onPress={() => navigation.popToTop()} /></View> );}export default TimerScreen;