I am looking to add a timer to my app which is built using react native.
I have looked at the link to the timer mixin in the documentation however I have built the rest of the app using es6 so this won't be compatible.
I have tried the below.
In my Main class I have a function called getTimerCountDown
getTimerCountDown() {
setTimeout(() => {
this.setTimeRemaining(this.getTimeRem()-1);
}, 1000);
}
getTimeRem() {
return this.state.timeRemaining;
}
I have tried calling this in componentDidUpdate as shown below. This works as I want it to if I don't make any other interactions with the UI.
If I do (eg I have a button I can click on the view.) as `componentDidUpdate gets called again the conunter gets really quick (as it is getting called x number of times)
componentDidUpdate(){
this.getTimerCountDown();
}
I am not sure if I am completly on the wrong track here or a small change to what I have done can get me what I want.
What is the best way to get a countdown timer working in react native using es6?
Timer Class
on main page
<Timer timeRem={this.getTimeRem()} />
returns
render(){
return (
<View style={styles.container}>
<Text> This is the Timer : {this.props.setTimer} - {this.props.timeRem} </Text>
</View>
)
}
I'm not really sure how that would work even without any other UI interactions. componentDidUpdate is called every time the component is re-rendered, something that happens when the internal state or passed down props have changed. Not something you can count on to happen exactly every second.
How about moving the getTimerCountDown to your componentDidMount method (which is only called once), and then using setInterval instead of setTimeout to make sure the counter is decremented continuously?
Kinda late, but you can try out this component I made for dealing with timers and es6 components in react-native:
https://github.com/fractaltech/react-native-timer
Idea is simple, maintaining and clearing timer variables on the components is a pain, so simply, maintain them in a separate module. Example:
// not using ES6 modules as babel has broken interop with commonjs for defaults
const timer = require('react-native-timer');
// timers maintained in the Map timer.timeouts
timer.setTimeout(name, fn, interval);
timer.clearTimeout(name);
// timers maintained in the Map timer.intervals
timer.setInterval(name, fn, interval);
timer.clearInterval(name);
// timers maintained in the Map timer.immediates
timer.setImmediate(name, fn);
timer.clearImmediate(name);
// timers maintained in the Map timer.animationFrames
timer.requestAnimationFrame(name, fn);
timer.cancelAnimationFrame(name);
Try this
Timer.js
import React, { Component } from "react";
import { View,Text,Button,StyleSheet } from "react-native";
const timer = () => {};
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
remainingTime: 10
};
}
countdownTimer(){
this.setState({remainingTime:10 });
clearInterval(timer);
timer = setInterval(() =>{
if(!this.state.remainingTime){
clearInterval(timer);
return false;
}
this.setState(prevState =>{
return {remainingTime: prevState.remainingTime - 1}});
},1000);
}
render() {
return (
<View style={styles.container}>
<Text>Remaining time :{this.state.remainingTime}</Text>
<Button title ="Start timer" onPress={()=>this.countdownTimer()}/>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
}
});
export default Timer;
App.js
import React, { Component } from "react";
import { View,Text,Button,StyleSheet } from "react-native";
import Timer from './timer';
export default class App extends Component{
render(
return (<Timer />)
);
}
Here is full code how you can create a timer (pomodoro Timer) in react-native;
Timer.js
import React from 'react'
import {Vibration, View, Button, Text, TextInput, StyleSheet} from 'react-native'
let pomInterval;
export default class Timer extends React.Component {
constructor() {
super();
this.state = {
minutes: 5,
seconds: 0,
workmins: 5,
worksecs: 0,
breakMins: 2,
breakSecs: 0,
timerState: 'WORK TIMER',
btnState: 'Start'
}
}
vibrate = () => {
Vibration.vibrate([500, 500, 500])
}
pomTimer = () => {
pomInterval = setInterval(() => {
let newSec = this.state.seconds;
newSec--;
if(newSec < 0) {
newSec = 59;
this.state.minutes--;
}
this.setState({
seconds: newSec,
})
if(newSec <= 0 && this.state.minutes <= 0) {
this.vibrate();
if(this.state.timerState == 'WORK TIMER') {
this.setState({
timerState: 'BREAK TIMER',
minutes: this.state.breakMins,
seconds: this.state.breakSecs
})
}else {
this.setState({
timerState: 'WORK TIMER',
minutes: this.state.workmins,
seconds: this.state.worksecs
})
}
}
}, 1000);
}
changeWorkMin = mins => {
clearInterval(pomInterval);
this.setState({
minutes: mins || 0,
workmins: mins || 0,
btnState: 'Start'
})
}
changeWorkSec = secs => {
clearInterval(pomInterval);
this.setState({
seconds: secs || 0,
worksecs: secs || 0,
btnState: 'Start'
})
}
changeBreakMin = mins => {
clearInterval(pomInterval);
this.setState({
breakMins: mins || 0,
btnState: 'Start'
})
}
changeBreakSec = secs => {
clearInterval(pomInterval);
this.setState({
breakSecs: secs || 0,
btnState: 'Start'
})
}
// Creating the functionality for the pause/start button
chnageBtnState = () => {
if(this.state.btnState == 'Start') {
this.pomTimer();
this.setState({
btnState: 'Pause'
})
}else {
clearInterval(pomInterval);
this.setState({
btnState: 'Start'
})
}
}
// Creating the functionality for the reset button
reset = () => {
clearInterval(pomInterval);
if(this.state.timerState == 'WORK TIMER') {
this.setState({
minutes: this.state.workmins,
seconds: this.state.worksecs,
btnState: 'Start'
})
}else {
this.setState({
minutes: this.state.breakMins,
seconds: this.state.breakSecs,
btnState: 'Start'
})
}
}
render() {
return (
<View style={styles.viewStyles}>
<Text style={styles.textStyles}>{this.state.timerState}</Text>
<Text style={styles.textStyles}>{this.state.minutes}:{this.state.seconds}</Text>
<Text>
<Button title={this.state.btnState} onPress={this.chnageBtnState} />
<Button title='Reset' onPress={this.reset} />
</Text>
<Text>Work Time:</Text>
<TextInput style={styles.inputStyles} value={this.state.workmins.toString()} placeholder='Work Minutes' onChangeText={this.changeWorkMin} keyboardType='numeric' />
<TextInput style={styles.inputStyles} value={this.state.worksecs.toString()} placeholder='Work Seconds' onChangeText={this.changeWorkSec} keyboardType='numeric' />
<Text>Break Time:</Text>
<TextInput style={styles.inputStyles} value={this.state.breakMins.toString()} placeholder='Break Minutes' onChangeText={this.changeBreakMin} keyboardType='numeric' />
<TextInput style={styles.inputStyles} value={this.state.breakSecs.toString()} placeholder='Break Seconds' onChangeText={this.changeBreakSec} keyboardType='numeric' />
</View>
)
}
}
// Creating a style sheet to write some styles
const styles = StyleSheet.create({
viewStyles: {
alignItems: 'center'
},
textStyles: {
fontSize: 48
},
inputStyles: {
paddingHorizontal: 50,
borderColor: 'black',
borderWidth: 1
}
})
App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Timer from './timer';
export default function App() {
return (
<View style={styles.container}>
<Timer />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
This is how we can create a pomodoro timer, a timer which has both WORK TIMER and BREAK TIMER and it vibrates the phone as one of the timer reaches its end. I also added the input functionality i.e, you can dynamically change the value of the minutes and seconds (whether work timer or break timer is going on). I also added a start/pause button and a reset button.
Related
Iam novice to react native programming (programming in genera) and for a project i have created a functional react component to display list of events data. In this component i want to sort the events first based on year and month and then based on days within these days. I created the algorithm for sorting the events and pasted the code for this on the useEffect hook of this react component. This component code is given below
import { ActivityIndicator, Dimensions, StyleSheet, Text, View } from 'react-native'
import React, { useContext, useEffect, useLayoutEffect, useState } from 'react'
import { Ionicons } from '#expo/vector-icons';
import { ScrollView, TouchableOpacity } from 'react-native-gesture-handler';
import { EventContext } from '../context/EventContext';
import EventCard from '../components/EventCard';
import { headerStyleOptions } from '../styles/headerStyleOptions';
import H1 from '../styles/styledcomponents/H1';
import { PRIMARYCOLOR } from '../styles/colors';
import { StatusBar } from 'expo-status-bar';
const { height, width } = Dimensions.get('window');
const monthsArray = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
let eventSorted: Object[] = [];
const EventListScreen = ({ navigation }) => {
const { state: events } = useContext(EventContext);
const [loading, setLoading] = useState(true);
useLayoutEffect(() => {
navigation.setOptions(headerStyleOptions('drawer', navigation, 'EVENTS LIST',
{ rightIcon1: 'add-circle', rightIconOnPress1: () => { navigation.navigate('EventCreateScreen') }, textColor: PRIMARYCOLOR, fontFamilly: 1, backgroundColor: 'black' }))
}, [])
useEffect(() => {
eventSorted = [];
events.map(ev => {
const date = new Date(ev.deadlinedate)
let key = `${date.getFullYear()}${date.getMonth()}`;
const dayKey = `${date.getDate()}`
if (eventSorted.length == 0) {
let obj: Object = {}
obj[key] = {};
eventSorted.push(obj)
eventSorted[0][key][dayKey] = []
eventSorted[0][key][dayKey].push(ev);
} else {
const check = eventSorted.findIndex(evso => ((Object.keys(evso)[0] == key)))
if (check == -1) {
let obj: Object = {}
obj[key] = {};
for (let i = 0; i < eventSorted.length; i++) {
if (date.getFullYear() == parseInt(Object.keys(eventSorted[i])[0].slice(0, 4))) {
if (date.getMonth() < parseInt(Object.keys(eventSorted[i])[0].slice(4))) {
eventSorted.splice(i, 0, obj)
eventSorted[i][key][dayKey] = []
eventSorted[i][key][dayKey].push(ev)
i = i + 1;
break;
} else {
if (i == eventSorted.length - 1) {
eventSorted.splice(i + 1, 0, obj)
eventSorted[i + 1][key][dayKey] = []
eventSorted[i + 1][key][dayKey].push(ev)
break;
}
}
} else if (date.getFullYear() < parseInt(Object.keys(eventSorted[i])[0].slice(0, 4))) {
eventSorted.splice(i, 0, obj)
eventSorted[i][key][dayKey] = []
eventSorted[i][key][dayKey].push(ev)
break;
}else{
if(i==eventSorted.length-1){
eventSorted.splice(i+1,0,obj)
eventSorted[i+1][key][dayKey] = [];
eventSorted[i+1][key][dayKey].push(ev)
break;
}
}
}
} else {
if (dayKey in eventSorted[check][key]) {
eventSorted[check][key][dayKey].push(ev)
} else {
Object.entries(eventSorted[check][key]).forEach(([k, value], index) => {
if (parseInt(k) > parseInt(dayKey)) {
let keyValues = Object.entries(eventSorted[check][key]);
keyValues.splice(index, 0, [dayKey, []])
eventSorted[check][key] = Object.fromEntries(keyValues)
eventSorted[check][key][dayKey].push(ev)
return false;
} else {
if (index == Object.keys(eventSorted[check][key]).length - 1) {
let keyValues = Object.entries(eventSorted[check][key]);
keyValues.splice(index + 1, 0, [dayKey, []])
eventSorted[check][key] = Object.fromEntries(keyValues)
eventSorted[check][key][dayKey].push(ev)
return false;
}
}
}
)
}
}
}
})
setTimeout(()=>{
console.log('finished2')
setLoading(false)
},0)
console.log('finished1')
console.log('eventSorted', eventSorted)
}, [])
return (
<View style={{ height,backgroundColor:'white'}}>
<StatusBar style='inverted' />
{loading ?
<View style={{flex:1,alignItems:'center',justifyContent:'center'}}>
<ActivityIndicator size='large'/>
</View>
:
// <Text>Hello</Text>
<ScrollView>
{
eventSorted.map(ev => {
return Object.keys(ev).map(key => {
let month: string;
month = monthsArray[parseInt(key.toString().slice(4))]
return (
<View>
<H1 key={`${month} ${key.substring(0,4)}`} style={{ alignSelf: 'center', fontSize: 22, paddingVertical: '2%' }}>{month} {key.substring(0, 4)}</H1>
<View>
{
Object.keys(ev[key]).map(k => {
let dateheight = height * .2 * Object.keys(ev[key][k]).length
return (
<View key={`${month} ${key.substring(0,4)} ${k}`}>
<View style={{ position: 'absolute', height: dateheight, width: 50, borderBottomWidth: 1, borderRightWidth: 1, backgroundColor: 'white' }}>
<Text style={{ alignSelf: 'center', fontSize: 20, top: 30 }}>{k}</Text>
</View>
{
ev[key][k].map(so => {
return <EventCard eventid={so.eventid} key={so.eventid}/>
})
}
</View>
)
}
)
}
</View>
</View>
)
})
})
}
<View style={{ alignSelf: 'center', backgroundColor: 'white', width: '100%' }}>
<Text style={{ fontSize: 100 }}></Text>
</View>
</ScrollView>
}
</View>
)
}
export default EventListScreen
const styles = StyleSheet.create({})
After running this code when i open this page there is a time lag to display the sorted event list. For checking where the code is taking time. I put a console.log on multiple locations in the code. You can see two console.logs at the end of the useEffect in adjacent two line which are console.log('finished1') and console.log('eventSorted', eventSorted). After inserting these two console logs when I run the page, I saw that finished1 is logging to the console immediately after navigating to this page but the next console.log is getting delayed like it is being awaited and it gets console logged and view gets populated at almost the same time. Can anyone tell me why eventSorted console log is behaving is it being awaited on?
Also When i interchange the position of the first console.log and second console.log. Then this console.log('finished1') is logging after a long delay after console.log('eventSorted',eventSorted) is output to the terminal.
You may be experiencing an adverse effect of not clearing setTimeout inside of useEffect - which is known to produce strange behavior.
Instead of
setTimeout(()=>{
console.log('finished2')
setLoading(false)
},0)
console.log('finished1')
console.log('eventSorted', eventSorted)
try
const timer = setTimeout(() => {
console.log("Hello, World!"), 3000);
setLoading(false)
},1000)
console.log('finished1')
console.log('eventSorted', eventSorted)
return () => clearTimeout(timer);
Also notice I have set the delay in setTimeout to 1 second (you had the delay set to 0 seconds in your code above).
I have a FlatList where I'm trying to scroll through each index of my data array every X amount of seconds. There's only two items in my array right now but there could be more. The current code works for the first two iterations but then it does not seem to reset properly and I get the scrollToIndex out of range error: index is 2 but maximum is 1. I would think that when the currentIndex is >= data.length my if statement would setCurrentIndex back to 0 but it doesn't seem to work. Basically what I'm trying to do is loop the items in the Flatlist automatically but each item pausing for a few seconds.
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow strict-local
*/
import 'react-native-gesture-handler';
import React, {useState, useEffect, useRef} from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator, HeaderBackButton } from '#react-navigation/stack';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
ImageBackground,
Image,
TextInput,
Button,
TouchableNativeFeedback,
TouchableWithoutFeedback,
TouchableOpacity,
Modal,
Pressable,
PanResponder,
FlatList,
Dimensions
} from 'react-native';
import { Immersive } from 'react-native-immersive';
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
import WineList from './screens/WineList';
import Home from './screens/Home';
import Rate from './screens/Rate';
import Thankyou from './screens/Thankyou';
const Stack = createStackNavigator();
const { width: windowWidth, height: windowHeight } = Dimensions.get("window");
const wineclub = require('./images/wineclub.png');
const gaspers = require('./images/gaspers.png');
const qrcode = require('./images/wineclubQR.png');
let ads = [
{
adImg: wineclub,
adTitle: 'Space will be limited so join online today!',
adInfo: ' Upon joining, both clubs will be billed our Trio Pre-Opening Promotion',
qrCodeImg: qrcode
},
{
adImg: gaspers,
adTitle: 'Coming Soon!',
adInfo: 'Gourmet chef designed menu. Stunning views. Modern romantic decor',
qrCodeImg: qrcode
}
]
function AdSlider({data}){
return(
<View style={{alignContent:'center', alignItems:'center', backgroundColor:'#4B4239', height:1400}}>
<Image source={data.adImg} style={{width:640,height:500}} ></Image>
<Text style={{color:'white', fontFamily:'LaoMN', fontSize:30, marginTop:20}}>{data.adTitle}</Text>
<Text style={{color:'white', fontFamily:'LaoMN', fontSize:20, marginTop:20, textAlign:'center'}} > {data.adInfo} </Text>
<View style={{flexDirection:'row', justifyContent:'flex-start', alignContent:'center', alignItems:'center', marginTop:20}}>
<Text style={{fontSize:40, color:'white', padding:20}}>Scan Here </Text>
<Image source={data.qrCodeImg}></Image>
</View>
</View>
)
}
const App: () => React$Node = () => {
Immersive.on()
Immersive.setImmersive(true)
const navigationRef = useRef(null);
const myRef = useRef(null);
const currentIndex = useRef(0);
const [modalVisible, setModalVisible] = useState(false);
const timerId = useRef(false);
const [timeForInactivityInSecond, setTimeForInactivityInSecond] = useState(
5
)
useEffect(() => {
resetInactivityTimeout()
},[])
const panResponder = React.useRef(
PanResponder.create({
onStartShouldSetPanResponderCapture: () => {
// console.log('user starts touch');
setModalVisible(false)
resetInactivityTimeout()
},
})
).current
const resetInactivityTimeout = () => {
clearTimeout(timerId.current)
timerId.current = setTimeout(() => {
// action after user has been detected idle
setModalVisible(true)
navigationRef.current?.navigate('Home');
}, timeForInactivityInSecond * 1000)
}
// for the slider
useEffect(() => {
const timer = setInterval(() => {
currentIndex.current = currentIndex.current === ads.length - 1
? 0
: currentIndex.current + 1;
myRef.current.scrollToIndex({
animated: true,
index: currentIndex.current ,
});
}, 5000);
return () => clearInterval(timer);
}, []);
return (
<NavigationContainer ref={navigationRef} >
<View {...panResponder.panHandlers} style={{ flex:1}}>
<TouchableWithoutFeedback >
<Modal
animationType="slide"
transparent={false}
hardwareAccelerated={false}
visible={modalVisible}
>
<FlatList
ref={myRef}
data={ads}
renderItem={({ item, index }) => {
return <AdSlider key={index} data={item} dataLength={ads.length} />;
}}
pagingEnabled
horizontal
showsHorizontalScrollIndicator={false}
/>
</Modal>
</TouchableWithoutFeedback>
<Stack.Navigator navigationOptions={{headerTintColor: '#ffffff',}} screenOptions={{
headerTintColor: '#ffffff',
cardStyle: { backgroundColor: '#4B4239' },
}} >
<Stack.Screen name="Home"
component={Home} options={{
headerShown: false,
}} />
<Stack.Screen name="WineList" component={WineList} options={{
title: 'Exit',
headerStyle: {
backgroundColor: '#4B4239',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}/>
<Stack.Screen name="Rate" component={Rate} options={{
title: 'Back to Selections',
headerStyle: {
backgroundColor: '#4B4239',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}/>
<Stack.Screen name="Thankyou" component={Thankyou}
options={
{
headerShown: false,
title: 'Home',
headerStyle: {
backgroundColor: '#4B4239',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}/>
</Stack.Navigator>
</View>
</NavigationContainer>
);
};
export default App;
You're getting this error because you are passing the item as data to the AdSlider component and it does not have any length property of course thus it returns undefined for data.length and that does not evaluate the expression currentIndex === data.length - 1 which it becomes currentIndex === undefined - 1 thus currentIndex will get increased by 1 without stopping and it will reach the value of 2 which is out of bounds.
There are several issues with your code.
You should not have a component inside another component and especially not when using effects and state from the parent component. Remove AdSlider outside of the App component.
You are passing item as data to the AdSlider and you are trying to fetch that as the data.length, which is obvious that it's not going to work because the data is the item which is an object and not an array.
You don't need to use the effects inside the AdSlider, set just one effect inside the App and change currentIndex to be a ref instead of a state variable because you don't need it's changing state in order to re-render because you're calling scrollToIndex for forcing the list to update and re-render.
Making it work using state and setTimeout
If you want to make the code wotk with currentIndex being in state (which you don't need), you can move effects inside the App component and change data.length with ads.length and it will work.
const App: () => React$Node = () => {
Immersive.on()
Immersive.setImmersive(true)
const navigationRef = useRef(null);
const myRef = useRef(null);
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
myRef.current.scrollToIndex({
animated: true,
index: currentIndex ,
});
}, [currentIndex]);
useEffect(()=> {
const timer = setTimeout(()=> {
// Change data.length to ads.length here
const nextIndex = currentIndex === ads.length - 1
? 0
: currentIndex + 1;
setCurrentIndex(nextIndex);
}, 5000);
return () => clearTimeout(timer);
}, [currentIndex]);
...
}
Making it work using ref and setInterval
Best thing to do though, is to convert currentIndex to a be a ref and use setInterval instead of setTimeout to have a looping timer call every 5 seconds:
const App: () => React$Node = () => {
Immersive.on()
Immersive.setImmersive(true)
const navigationRef = useRef(null);
const myRef = useRef(null);
// Make currentIndex a ref instead of a state variable,
// because we don't need the re-renders
// nor to trigger any effects depending on it
const currentIndex = useRef(0);
useEffect(() => {
// Have a timer call the function every 5 seconds using setInterval
const timer = setInterval(() => {
// Change data.length to ads.length here
currentIndex.current = currentIndex.current === ads.length - 1
? 0
: currentIndex.current + 1;
myRef.current.scrollToIndex({
animated: true,
index: currentIndex.current,
});
}, 5000);
return () => clearInterval(timer);
}, []);
...
}
You can check a working Expo Snack here.
looks like your if statement is incorrect, the maximum index should be totalLength - 1.
for example, we have an array of 3 items: [{id: 1, index: 0}, {id: 2, index: 1}, {id: 3, index: 2}], then the length of the array is 3, but the maximum index is 2, so when the current index is ">= 2 (totalLength - 1)", you should reset it to 0. and for the else conditions, set next index to 'currentIdx + 1'
if(activeIdx === ITEMS.length - 1){
setActiveIdx(0)
} else {
setActiveIdx(idx => idx + 1);
}
for more detailed, code may look like this:
function Slider(props) {
const ref = React.useRef(null);
const [activeIdx, setActiveIdx] = React.useState(0)
React.useEffect(() => {
ref.current.scroll({left: ITEM_WIDTH * activeIdx, behavior: "smooth"}) // please use .scrollToIndex here
}, [activeIdx]);
React.useEffect(() => {
let timer = setTimeout(() => {
let nextIdx = activeIdx === ITEMS.length - 1 ? 0 : activeIdx + 1;
setActiveIdx(nextIdx)
}, 3000);
return () => clearTimeout(timer)
}, [activeIdx]);
return (...)
}
For some reason I'm not able to see what I'm doing wrong with my code. I seem to be using Animated just as the documentation shows but this error keeps coming. The code snippet:
import React ,{ Component} from "react";
import { Card } from 'react-native-elements';
import { Text, View ,Animated,Easing} from 'react-native';
import { connect } from 'react-redux';
import { baseUrl } from '../shared/baseUrl';
import { Loading } from './LoadingComponent';
const mapStateToProps = state => {
return {
dishes: state.dishes,
comments: state.comments,
promotions: state.promotions,
leaders: state.leaders
}
}
function RenderItem(props) {
const item = props.item;
if (item != null) {
return(
<Card
featuredTitle={item.name}
featuredSubtitle={item.designation}
image={{uri: baseUrl + item.image}}>
<Text
style={{margin: 10}}>
{item.description}</Text>
</Card>
);
}
}
class Home extends Component{
constructor(props) {
super(props);
this.animatedValue = new Animated.Value(0);
}
componentDidMount () {
this.animate()
}
animate () {
this.animatedValue.setValue(0)
Animated.timing(
this.animatedValue,
{
toValue: 8,
duration: 8000,
easing: Easing.linear
}
).start(() => this.animate())
}
render() {
const xpos1 = this.animatedValue.interpolate({
inputRange:[0,1,3,5,8],
outputRange:[1200 , 600 , 0 , -600 , -1200]
});
return(
<View style = {{flex :1 , flexDirection:'row' , justifyContent : 'center'}}>
<Animated.View style={{width :'100%' , tranform :[{translateX:xpos1}]}}>
<RenderItem item={this.props.dishes.dishes.filter((dish) => dish.featured)[0]}
isLoading={this.props.dishes.isLoading}
erreMess={this.props.dishes.erreMess}
/>
</Animated.View>
</View>
);
}
}
export default connect(mapStateToProps)(Home);
I am trying to implement animation.timing function . As mentioned in documentation I wrote animation.timing function with animatedValue declared in constructor. But when I wrote animate function I got the following error :
I followed documentation of react animation and tried everything on internet to solve the issue but failed .
I think you may just want to loop the animation versus trying to manually "reset" the time, which is recommended against in the documentation.
loop
Loops a given animation continuously, so that each time it reaches the
end, it resets and begins again from the start.
constructor(props) {
super(props);
this.state = {
animatedValue: new Animated.Value(0),
}
}
componentDidMount () {
this.animate();
}
animate = () => {
Animated.loop(
Animated.timing(
this.state.animatedValue,
{
useNativeDriver: true, // loop W/O blocking main JS thread
toValue: 8,
duration: 8000,
easing: Easing.linear
}
),
).start()
}
I am trying to make a simple clock that starts and stops on the press of a button. Here I have set a variable equal to a setInterval so that I can clear it later on. But for some reason, it is being called without the button being pressed.
Here the autoInc should have been called ideally when I pressed the "Start" button but it gets called anyway.
import React, { Component } from "react";
import { Text, View, Button } from "react-native";
export default class Counter extends Component {
increaser = () => {
this.setState(prePre => {
return { counter: prePre.counter + 1 };
});
};
constructor(props) {
super(props);
this.state = {
counter: 0,
wantToShow: true
};
autoInc = setInterval(this.increaser, 1000);
}
render() {
if (this.state.wantToShow)
return (
<View>
<Text style={{ color: "red", fontSize: 50, textAlign: "center" }}>
{this.state.counter}
</Text>
<Button title="Start" onPress={this.autoInc} />
</View>
);
}
}
A full react example here, you just have to translate functions in react native.
Create a variable this.interval=null in your constructor, assign to this the interval in the startFn , then just remove it with window.clearInterval(this.interval);
export default class App extends Component {
constructor(props) {
super(props);
this.interval = null;
}
componentDidMount() {}
startFn = () => {
console.log("assign and start the interval");
this.interval = setInterval(this.callbackFn, 1000);
};
stopFn = () => {
console.log("clear interval");
window.clearInterval(this.interval);
};
callbackFn = () => {
console.log("interval callback function");
};
render(props, { results = [] }) {
return (
<div>
<h1>Example</h1>
<button onClick={this.startFn}>start Interval</button>
<button onClick={this.stopFn}>stop Interval</button>
</div>
);
}
}
Codesandbox example here: https://codesandbox.io/s/j737qj23r5
In your constructor, you call setInterval(this.increaser,1000) and then assign its return value to the global property autoInc.
this.autoInc is undefined in your render function.
It looks like
autoInc = setInterval(this.increaser,1000)
is simply incorrectly written and what you want instead is:
this.autoInc = () => setInterval(this.increaser,1000)
I have the attached React-Native app which I'm building for a class project. My START/STOP toggle button is in the parent view and my timer function is in my child view. I have the clock ticking down and starting and stopping but I had to update the parent from the child every time we got a time change.
My question is wouldn't it be better if we updated the parent only when the toggle was pressed? Basically, that would mean being able to retrieve data from a child from within the parent. I just can't figure out how to do that or if it's even possible
import React from 'react';
import { Button,StyleSheet, Text, View } from 'react-native';
import ignoreWarnings from 'react-native-ignore-warnings';
ignoreWarnings(['Warning: componentWillMount is deprecated',
'Warning: componentWillReceiveProps is deprecated'])
import PropTypes from 'prop-types'
import { Constants } from 'expo';
import { Card } from 'react-native-elements';
function merge(obj, dict){
console.log('inside merge')
Object.entries(dict).forEach(([k, v]) =>{
if (!obj[k] || typeof v !== 'object') obj[k] = v
else merge(obj[k], v)
})
}
export class Timers extends React.Component{
constructor(props){
super(props)
this.state={name: this.props.name,
min: this.props.min,
sec: this.props.sec,
timer:this.props.timer,
startFlag:this.props.startFlag,
switchFlag:this.props.switchFlag,
currentName:this.props.currentName,}
}
mountTimer(){
// console.log('timer mounted')
// this.interval=setInterval(this.inc,10000)
// console.log(this.interval)
}
clearTimer(){
// console.log('timer cleared')
clearInterval(this.interval)
}
componentWillMount(){
// console.log('will mount timers')
}
componentDidMount(){
// console.log('mount1')
dictionary=this.state
this.props.updateParent(dictionary)
if(this.state.name === 'WORK'){
// console.log('work timer launched')
this.interval=setInterval(this.inc,1000)}
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log('STATIC',nextProps)
return{name: nextProps.name,
min: nextProps.min,
sec: nextProps.sec,
timer:nextProps.timer,
startFlag:nextProps.startFlag,
switchFlag:nextProps.switchFlag,
currentName:nextProps.currentName,}
// Return null to indicate no change to state.
}
shouldComponentUpdate(nextProps,nextState){
console.log('shouldCompnentUpdate')
// if start is false then the timer should be updating
return this.state.timer
}
componentWillUnmount(){
console.log('unmount')
clearInterval(this.interval)
}
inc=()=>{
console.log('TIC',this.state)
if(this.state.startFlag){
if(this.state.sec > 0){
this.setState({sec: String(parseInt(this.state.sec - 1))},function(){
let dictionary={left:{sec:this.state.sec}}
this.props.updateParent(dictionary)
})
}else{
this.setState({sec:'5'})
if (this.state.min > 0){
this.setState({min: String(parseInt(this.state.min - 1))},function(){
let dictionary={left:{min:this.state.min,
sec:5}}
this.props.updateParent(dictionary)
})
}else{
this.setState({min:'9',sec:'0'})
console.log('reset123')
this.clearTimer()
}
}
}
}
render(){
return(
<View style={styles.timer}>
<Text>{this.state.name}</Text>
<Text>{'min: ' + this.state.min + ' sec: ' + this.state.sec }</Text>
<Text>TBD</Text>
</View>
)
}
}
export default class App extends React.Component {
constructor(){
super()
this.state={left:{name:'WORK',
min:2,
sec:0,
timer:false,},
right:{name:'PLAY',
min:2,
sec:0,
timer:false,},
switchSide:false,
currentName:'WORK',
start:false,}
}
updateState(dictionary){
console.log('UPDATE PARENT')
let stateCopy={...this.state}
/* Object.entries(dictionary).map(([key,value])=>{
stateCopy[key] = value.toString()
}) */
console.log('DICTIONARY',dictionary)
merge(stateCopy, dictionary)
this.setState({...stateCopy},function(){
console.log('LEFTLEFTLEFT',this.state,'LEFTSEC')
console.log(this.state.left.min,'LEFTMIN')
})
// console.log(this.state.leftSec,'LEFTSEC')
// })
}
shouldComponentUpdate(nextProps,nextState){
console.log('SHOULD in main app')
return true
}
startToggle(){
if(this.state.currentName === 'WORK'){
stateCopy={...this.state}
stateCopy.left.timer=!this.state.left.timer
stateCopy.start=!this.state.start
console.log('STATECOPY',stateCopy)
this.setState({...stateCopy})
}
console.log('START TOGGLE START TOGGLE',this.state)
}
resetToggle(){
console.log('reset')
}
componentWillMount(){
console.log('will mount main APP')
}
render() {
console.log('RENDER',this.state,'TIMER',this.state.left.timer)
return (
<View style={styles.container}>
<Button title={this.state.start ? 'Stop' : 'Start'} onPress=
{()=>this.startToggle()}/>
<View style={styles.row}>
<Timers
name = {this.state.left.name}
min = {this.state.left.min}
sec = {this.state.left.sec}
timer= {this.state.left.timer}
startFlag = {this.state.start}
switchFlag = {this.state.switchSide}
currentName= {this.state.currentName}
updateParent={(dictionary={})=>{this.updateState(dictionary)}} >
</Timers>
<Timers
name = {this.state.right.name}
min = {this.state.right.min}
sec = {this.state.right.sec}
timer= {this.state.right.timer}
startFlag = {this.state.start}
switchFlag = {this.state.switchSide}
currentName= {this.state.currentName}
updateParent={(dictionary={})=>{this.updateState(dictionary)}} >
</Timers>
</View>
<Button style={styles.top} title='Reset'onPress=
{()=>this.resetToggle()}/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection:'column',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
timer:{
flex:1,
flexDirection:'column',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
row:{
flex:0,
flexDirection:'row',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginTop:55,
marginBottom:55,
},
top:{
},
})
not sure how this works yet, but it appears the answer is to use ref's. I found the attached example by rossipedia
import React, { Component } from 'react';
import { render } from 'react-dom';
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
render(<Parent />, document.getElementById('root'));