React Native : executing function multiple times when pressing a button - react-native

What I want to do is executing function multiple times when pressing the button.
<Button onPress={()=>{console.log('execute')}}/>
If I use onPress, the function is executed only once, even if I does not press out.
But I hope console.log is executed multiple times in sequence (or every seconds) until press out.

You can use TouchableOpacity. In this example addOne function will execute every second.
export default class App extends Component{
constructor() {
super();
this.state = {
number: 0,
};
this.timer = null;
this.addOne = this.addOne.bind(this);
this.stopTimer = this.stopTimer.bind(this);
}
addOne() {
this.setState({number: this.state.number+1});
this.timer = setTimeout(this.addOne, 1000);
}
stopTimer() {
clearTimeout(this.timer);
}
render(){
return (
<View style={styles.container}>
<TouchableOpacity onPressIn={this.addOne} onPressOut={this.stopTimer}>
<Text>Press me</Text>
<Text>{this.state.number}</Text>
</TouchableOpacity>
</View>
);
}
}

This is how I track how many times someone pressed with hooks:
const [lastPressed, setLastPressed] = useState(0);
const [pressedAmount, setPressedAmount] = useState(0);
const handlePress = useCallback(() => {
const time = new Date().getTime();
const delta = time - lastPressed;
setLastPressed(time);
if (lastPressed) {
if (delta < DOUBLE_PRESS_DELAY) {
setPressedAmount(pressedAmount + 1);
} else {
setPressedAmount(0);
}
}
}, [lastPressed]);

Related

React Native inactivity logout

Hello i want to make an inactivity log out for my app, so if the user doesn't do anything on the app for 3 minutes, the app will comeback to the login screen.
I'm using expo, react native navigation V6, and functional components.
i haven't been able to figure how to do it. please help.
I think i was able to do that on an app that i made 1 year ago, i think this code can you help you.
const ManageExpenses = ({ route, navigation }) => {
const [time, setTime] = useState(0);
useEffect(() => {
let mounted = true;
if (mounted) {
tick();
}
return () => mounted = false;
}, []);
function tick() {
let timer = setInterval(() => {
setTime((prevTime) => (prevTime = prevTime + 1));
}, 1000);
}
if (time >= 10) {
navigation.goBack();
}
function pressHandler() {
setTime(0)
}
return (
<Pressable onPress={pressHandler} style={styles.container}>
</Pressable>
);
export default ManageExpenses;
const styles = StyleSheet.create({
container: {
flex: 1,
}
})
I created a Pressable component around the entire screen and it redefine the time state when the user press the screen. I hope this will help you!

i am working on react native expo project (background fetch time)

I'm getting the time one time. sometimes I didn't get that too. I need the background time. once if I started the fetching background button it should give the time in the console until I stop the fetch background. I used expo background fetch and task manager to fetch the background time . I'm facing a problem it fetching time continuously so please help me with this coding. documentation is in expo documentation.
import * as BackgroundFetch from 'expo-background-fetch';
import * as TaskManager from 'expo-task-manager';
const BACKGROUND_FETCH_TASK = 'background-fetch1';
const now = Date.now();
console.log(`Got background fetch call at**** date: ${new Date(now).toISOString()}`);
// Be sure to return the successful result type!
return BackgroundFetch.Result.NewData;
});
async function registerBackgroundFetchAsync() {
const now = Date.now();
console.log(`Registered**** date: ${new Date(now).toISOString()}`);
console.log(" registered ");
return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, {
minimumInterval: 60 * 15, // 30 sec
stopOnTerminate: false, // android only,
startOnBoot: true, // android only
});
}
async function unregisterBackgroundFetchAsync() {
console.log("un registered ");
const now = Date.now();
console.log(`Un registered fetch call at**** date: ${new Date(now).toISOString()}`);
return BackgroundFetch.unregisterTaskAsync(BACKGROUND_FETCH_TASK);
}
const TrackScreen = ({ navigation }) => {
const { state, clearError, fetchContactsforTrack } = useContext(TrackContext);
const [isRegistered, setIsRegistered] = useState(false);
const [status, setStatus] =useState(BackgroundFetch.Status);
// Clear error if any
useEffect(() => {
checkStatusAsync();
const unsubscribe = navigation.addListener('focus', () => {
clearError();
});
return unsubscribe;
}, [navigation, clearError]);
/* Stop the backgroud tracking */
const stopTracking=()=>{
}
/* Stop the backgroud tracking */
const startTracking=(track)=>{
var trackdata= fetchContactsforTrack(track)
}
const checkStatusAsync = async () => {
const status = await BackgroundFetch.getStatusAsync();
const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_FETCH_TASK);
const now = Date.now();
console.log(`Checking statuscall at**** date: ${new Date(now).toISOString()}`);
console.log("-------------"+status);
console.log("-------------"+isRegistered);
console.log("-------------"+JSON.stringify(BackgroundFetch));
setStatus(status);
setIsRegistered(isRegistered);
};
const toggleFetchTask = async () => {
if (isRegistered) {
await unregisterBackgroundFetchAsync();
} else {
await registerBackgroundFetchAsync();
await BackgroundFetch.setMinimumIntervalAsync(.5);
}
checkStatusAsync();
};
return (
<View >
<View >
<Text>
Background fetch status:{' '}
<Text >{status ? BackgroundFetch.Status[status] : null}</Text>
</Text>
<Text>
Background fetch task name:{' '}
<Text >
{isRegistered ? BACKGROUND_FETCH_TASK : 'Not registered yet!'}
</Text>
</Text>
</View>
<View ></View>
<Button
title={isRegistered ? 'Unregister BackgroundFetch task' : 'Register BackgroundFetch task'}
onPress={toggleFetchTask}
/>
</View>
);
this is my code
Your function might not run immediately after a second or so. This is because the minimum interval of time is 15 minutes. Android automatically switches it to 15 minutes if the value of the interval is less than the minimum.
Maybe this would help - https://proandroiddev.com/android-restrictions-you-may-encounter-during-development-process-c39ede513813

How to call an async method in render method from Expo?

I saved data to AsyncStorage. Now I want to show all data from AsyncStorage in separate screen.
Method getData is async method. It reads from AsyncStorage.
I use code like that
import React from "react";
class List extends React.Component {
state = { list: null };
async componentDidMount() {
const list = await getData("List");
console.log('LIST: ' + JSON.stringify(list));
this.setState({ list });
}
render() {
const { list } = this.state;
console.log('state: ' + JSON.stringify(list));
if(list || list.length <= 0)
return (<View><Text>Empty.</Text></View>);
return (
<View>
{ list.map(item => (
<Text tabLabel={item}>{item}</Text>
))}
</View>
);
}
}
When I run that code, i get 2 console messages:
state: []
and
LIST: [{item1}, {item2}...]
It means that componentDidMount fires after render method, that is why UI is empty.
How can I change this. I need to read data from AsyncStorage and show it in UI.
Thank you.
componentDidMount as the title says is meant to run after render. To achieve what you want to do you can use componentWillMount.
import React from 'react';
class List extends React.Component {
state = { list: null };
componentWillMount() {
const loadData = async () => {
const list = await getData('List');
console.log('LIST: ' + JSON.stringify(list));
this.setState({ list });
};
loadData();
}
render() {
const { list } = this.state;
console.log('state: ' + JSON.stringify(list));
if (list || list.length <= 0)
return (
<View>
<Text>Empty.</Text>
</View>
);
return (
<View>
{list.map((item) => (
<Text tabLabel={item}>{item}</Text>
))}
</View>
);
}
}

State changes in a react application with mobX

My task is to show the download component when data from the server has not yet arrived.
export const LoaderComponent = () => (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
const styles = StyleSheet.create({
center: {
.....
},
});
I created a state file to display the boot component.
import { observable, action } from 'mobx';
class LoaderState {
#observable loading: boolean = true;
#action showLoader() {
this.loading = true;
}
#action hideLoader() {
this.loading = false;
}
}
export default new LoaderState();
When authorizing the user, I display the download component, after receiving data from the server, I hide the download component. I made an artificial delay of two seconds.
class AuthState {
#observable email: string = '';
#observable password: string = '';
#action authentication(data: IAuth) {
console.log('Action authentication');
LoaderState.showLoader();
....
setTimeout(() => {
LoaderState.hideLoader();
console.log('Change state loader', LoaderState.loading);
}, 2000);
}
}
export default new AuthState();
On the next screen, I check if the download flag is set, I show the download component, and if not, I hide it.
export const ProvidersScreen = () => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
};
The problem is that the download component is always shown and when the state changes, it is not hidden. Why is the download component not hiding?
I think the reason is your ProvidersScreen is not an observer component, so try it:
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});
You forgot to add observer
Add below code:
import { observer } from "mobx-react";
export const ProvidersScreen = observer(() => {
console.log(LoaderState.loading);
if (LoaderState.loading) {
return <LoaderComponent />;
}
return (
<View>
....
</View>
);
});

Countdown timer in react-native

I want to countdown from 3 to 1 when a screen is loaded in react-native. I tried it with setTimeOut like this and it didn't work. What am I doing wrong here? How can I achieve this? When the screen is loaded, I want to show 3 =-> 2 ==> 1 with 1 second interval. Here is my code.
constructor(props) {
super(props);
this.state = {
timer: 3
}
}
// componentDidMount
componentDidMount() {
setTimeout(() => {
this.setState({
timer: --this.state.timer
})
}, 1000);
}
In your code setTimeout is called in componentDidMount and ComponetDidMount will be called once in whole component lifeCycle. So, the function within setTimeout will be called once only. i.e. just after the first render but upon successive render, the componentDidMount won't be called.
Solution to your problem can be:
1. Class Component
constructor(props: Object) {
super(props);
this.state ={ timer: 3}
}
componentDidMount(){
this.interval = setInterval(
() => this.setState((prevState)=> ({ timer: prevState.timer - 1 })),
1000
);
}
componentDidUpdate(){
if(this.state.timer === 1){
clearInterval(this.interval);
}
}
componentWillUnmount(){
clearInterval(this.interval);
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', }}>
<Text> {this.state.timer} </Text>
</View>
)
}
'setInterval' vs 'setTimeout'
Advantage of using a function in setState instead of an object
memory leak because of setInterval:
if we unmount the component before clearInterval called, there is a memory leak because the interval that is set when we start and the timer is not stopped. React provides the componentWillUnmount lifecycle method as an opportunity to clear anything that needs to be cleared when the component is unmounted or removed.
2. Functional Component
function CountDownTimer(props) {
const [time, setTime] = React.useState(props.initialValue || 10);
const timerRef = React.useRef(time);
React.useEffect(() => {
const timerId = setInterval(() => {
timerRef.current -= 1;
if (timerRef.current < 0) {
clearInterval(timerId);
} else {
setTime(timerRef.current);
}
}, 1000);
return () => {
clearInterval(timerId);
};
}, []);
return (
<View style={{ flex: 1, justifyContent: 'center' }}>
<Text> {time} </Text>
</View>
)
}
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component. So on component rerender the object reference will be same.
Answers given by #TheEhsanSarshar and #Rishabh Jain will also work. I have shown a slightly different solution from the others.
Updated Hooks (using useEffect) version to countdown using setInterval in react-native:
const [timerCount, setTimer] = useState(60)
useEffect(() => {
let interval = setInterval(() => {
setTimer(lastTimerCount => {
lastTimerCount <= 1 && clearInterval(interval)
return lastTimerCount - 1
})
}, 1000) //each count lasts for a second
//cleanup the interval on complete
return () => clearInterval(interval)
}, []);
use the state variable timerCount as: <Text>{timerCount}</Text>
Usage:
timestamp prop must be in seconds
const refTimer = useRef();
const timerCallbackFunc = timerFlag => {
// Setting timer flag to finished
console.warn(
'You can alert the user by letting him know that Timer is out.',
);
};
<Timer
ref={refTimer}
timestamp={moment(item?.time_left).diff(moment(), 'seconds')}
timerCallback={timerCallbackFunc}
textStyle={styles.timerTextAHL}
/>
Timer.js
import React, {
useState,
useEffect,
useRef,
forwardRef,
useImperativeHandle,
} from 'react';
import { Text, View } from 'react-native';
const Timer = forwardRef((props, ref) => {
// For Total seconds
const [timeStamp, setTimeStamp] = useState(
props.timestamp ? props.timestamp : 0,
);
// Delay Required
const [delay, setDelay] = useState(props.delay ? props.delay : 1000);
// Flag for informing parent component when timer is over
const [sendOnce, setSendOnce] = useState(true);
// Flag for final display time format
const [finalDisplayTime, setFinalDisplayTime] = useState('');
useInterval(() => {
if (timeStamp > 0) {
setTimeStamp(timeStamp - 1);
} else if (sendOnce) {
if (props.timerCallback) {
props.timerCallback(true);
} else {
console.log('Please pass a callback function...');
}
setSendOnce(false);
}
setFinalDisplayTime(secondsToDhms(timeStamp));
}, delay);
function secondsToDhms(seconds) {
seconds = Number(seconds);
var d = Math.floor(seconds / (3600 * 24));
var h = Math.floor((seconds % (3600 * 24)) / 3600);
var m = Math.floor((seconds % 3600) / 60);
var s = Math.floor(seconds % 60);
var dDisplay = d > 0 ? d + 'd ' : '';
var hDisplay = h > 0 ? h + 'h ' : '';
var mDisplay = m > 0 ? m + 'm ' : '';
var sDisplay = s > 0 ? s + 's ' : '';
return dDisplay + hDisplay + mDisplay + sDisplay;
}
const refTimer = useRef();
useImperativeHandle(ref, () => ({
resetTimer: () => {
// Clearing days, hours, minutes and seconds
// Clearing Timestamp
setTimeStamp(props.timestamp);
setSendOnce(true);
},
}));
return (
<View ref={refTimer} style={props.containerStyle}>
<Text style={props.textStyle}>{sendOnce ? finalDisplayTime : '0'}</Text>
</View>
);
});
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest function.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
const id = setInterval(tick, delay);
return () => {
clearInterval(id);
};
}
}, [delay]);
}
export default Timer;
The hooks version.
function CountDown() {
const [count, setCount] = useState(3)
useEffect(() =>
let interval = setInterVal(() => {
setCount(prev => {
if(prev === 1) clearInterval(interval)
return prev - 1
})
})
// interval cleanup on component unmount
return () => clearInterval(interval)
), [])
return <Text>{count}</Text>
}
If anyone wants to start the timer again on a button press, this will be the code in react-hooks:
let timer = () => {};
const myTimer = () => {
const [timeLeft, setTimeLeft] = useState(30);
const startTimer = () => {
timer = setTimeout(() => {
if(timeLeft <= 0){
clearTimeout(timer);
return false;
}
setTimeLeft(timeLeft-1);
}, 1000)
}
useEffect(() => {
startTimer();
return () => clearTimeout(timer);
});
const start = () => {
setTimeLeft(30);
clearTimeout(timer);
startTimer();
}
return (
<View style={styles.container}>
<Text style={styles.timer}>{timeLeft}</Text>
<Button onPress={start} title="Press"/>
</View>
)}
Here in this example, I have taken a timer of 30 seconds
Code Of Power
Hope so this Way is Easy
componentDidMount() {
this.CounterInterval()
}
CounterInterval = () => {
this.interval = setInterval(
() => this.setState({
timer: this.state.timer - 1
}, () => {
if (this.state.timer === 0) {
clearInterval(this.interval);
}
}),
1000
);
}