exit app after second back press using toast - react-native

This exitAlert is call after android button is press, I want to disable exit after the toast is close, since the toast has no close event, I am using timeout to disable it, obviously the code below does not disable second press exit:
const exitAlert = () => {
const duration = 3 * 1000;
showToast('Press again to exit', duration, 'top');
BackHandler.addEventListener('hardwareBackPress', () => {
BackHandler.exitApp();
});
setTimeout(() => BackHandler.removeEventListener('hardwareBackPress', () => {}),
duration);
}
Okay this works:
let pressTwice = true;
const duration = 3 * 1000;
showToast('Confirm exit', duration, 'top');
BackHandler.addEventListener('hardwareBackPress', () => {
if (pressTwice) {
BackHandler.exitApp();
}
});
setTimeout(function() {
pressTwice = false;
}, duration);

This may work
componentDidMount(){
let oncePressed = false;
const duration = 3 * 1000;
BackHandler.addEventListener('hardwareBackPress', () => {
if(oncePressed){
oncePressed = false;
BackHandler.exitApp();
}else{
showToast('Press again to exit', duration, 'top');
oncePressed = true
setTimeout(() => BackHandler.removeEventListener('hardwareBackPress', () => {
oncePressed = true
}),
duration);
}
});
}

Related

Currently, it continues to run after 1min. I want it stop if the condition is met

Below, I want to clearInterval if processingStatus[0] === "DELIVERED".
useEffect(() => {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
// Fetching data changes after 1 Minute
const interval = setInterval(() => {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
// nextStep();
}, 60000);
if (processingStatus[0] === "DELIVERED") {
return () => clearInterval(interval);
}
}, [dispatch]);
You can use useRef(), like this
const YourComponent = () => {
const processingStatus = useSelector(...);
const processingStatusRef = useRef(null);
processingStatusRef.current = processingStatus[0];
useEffect(() => {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
// Fetching data changes after 1 Minute
const interval = setInterval(() => {
if (processingStatusRef.current === "DELIVERED") {
clearInterval(interval)
} else {
dispatch(fetchDeliveryStatus({ client_id: user.id }));
}
}, 60000);
return () => clearInterval(interval);
}, [dispatch]);
}

Close React Native apps / dispatch redux action when running on background

I want to create a function inside my components that detect when the app is in the background for 30 seconds, I want to dispatch a logout action or close the apps. is that possible if we do that in react native?
I'm using hooks
Thanks,
update :
I'm using the wowandy's solution but the thing is if user close the apps for less than 10 seconds and then open the app again, the dispatch command will still be executed in 30 seconds. is there any way to cancel the timeout ?
useEffect(() => {
let timeout;
const subscription = AppState.addEventListener('change', (nextAppState) => {
clearTimeout(timeout);
if (appState.current === 'background') {
timeout = setTimeout(() => dispatch(removeDataLogin()), 30000);
}
appState.current = nextAppState;
});
return () => {
subscription.remove();
clearTimeout(timeout);
};
}, []);
Update 3
So I tried to use Michael Bahl's solution as commented below. it works great with timestamp.
useEffect(() => {
let start;
let end;
const subscription = AppState.addEventListener("change", nextAppState => {
if (
appState.current.match(/inactive|background/) &&
nextAppState === "active"
) {
end = new Date()
let ms = moment(end).diff(moment(start))
if (Number(ms) >= 30000) {
dispatch(removeDataLogin())
} else {
}
} else {
start = new Date()
console.log('start diff :', start)
}
appState.current = nextAppState;
setAppStateVisible(appState.current);
console.log("AppState", appState.current);
});
return () => {
subscription.remove();
};
}, []);
update 3 Im using Michael Bahl's solution so I created a timestamp that check the difference between inactive and active screens, then dispatch the redux action
useEffect(() => {
let start;
let end;
const subscription = AppState.addEventListener("change", nextAppState => {
if (
appState.current.match(/inactive|background/) &&
nextAppState === "active"
) {
console.log('end =====')
console.log('start diff == ', start)
end = new Date()
console.log('end diff ===', end)
let ms = moment(end).diff(moment(start))
console.log('different : ', ms)
console.log(typeof ms)
if (Number(ms) >= 30000) {
console.log('harusnya logout')
dispatch(removeDataLogin())
} else {
console.log(ms, 'masuk sini')
}
} else {
start = new Date()
console.log('start diff :', start)
}
appState.current = nextAppState;
setAppStateVisible(appState.current);
console.log("AppState", appState.current);
});
return () => {
subscription.remove();
};
}, []);
You can handle app state using AppState and close it with BackHandler
See example:
import React, {useRef, useEffect} from 'react';
import {AppState, BackHandler} from 'react-native';
const App = () => {
const appState = useRef(AppState.currentState);
useEffect(() => {
let timeout;
const subscription = AppState.addEventListener('change', (nextAppState) => {
clearTimeout(timeout);
if (appState.current === 'background') {
timeout = setTimeout(() => BackHandler.exitApp(), 30000);
}
appState.current = nextAppState;
});
return () => {
subscription.remove();
clearTimeout(timeout);
};
}, []);
// TODO
return /* TODO */;
};

react-native why I get memory leak if I use this useEffect method

why I get memomy leak if I use this code:
useEffect(() => {
if(step === 2) {
BackHandler.addEventListener('hardwareBackPress', () => handleStep1WithBackhandler());
return () => {
BackHandler.removeEventListener('hardwareBackPress', () => handleStep1WithBackhandler());
}
} else {
if(hardware === true) {
BackHandler.addEventListener('hardwareBackPress', () => false);
return () => {
BackHandler.removeEventListener('hardwareBackPress', () => false);
}
}
}
}, [step]);
if step is equal to 2 then I go back to step1 with the function. Else nothing.
Whats wrong with that?
May be due to arrow functions in addEventListener and removeEventListener
In addition to the value of the step within the eventListener you can use this approach:
Create a customHook for tracking a state to a ref.
const useStateRef = (defaultValue = null)=> {
const [value, setValue] = useState(defaulValue)
const ref = useRef()
useEffect(()=> {
ref.current = value
},[value])
return [value, setValue, ref]
}
and use it as follows
const SomeComponent = () => {
const [step, setStep, stepRef] = useStateRef(1)
const handleBackPress = React.useCallBack(() => {
if (stepRef.current === 2) {
//some logic
}
if (someWhere.hardware) {
//some logic
}
},[])
useEffect(()=> {
BackHandler.addEventListener('hadwareBackPress',handleBackPress)
return ()=> BackHandler.removeEventListener('hadwareBackPress',handleBackPress)
},[])
//some code ...
//return something ...
return null
}

Slider not keep on moving with onProgress method in react native

I am playing some audio files in react native. For progress of the audio file (Duration), I am showing slider for status of the audio file and for forward and reverse the duration.
But, According to audio duration, It is not keep on moving the position (like timer).
https://www.npmjs.com/package/react-native-slider
getInfo = async () => {
try {
const info = await SoundPlayer.getInfo();
// console.log('getInfo', info); // {duration: 12.416, currentTime: 7.691}
const currentTime = get(info, 'currentTime');
this.setState({ currentDuration: currentTime });
} catch (e) {
console.log('There is no song playing', e);
}
}
getProgress = (e) => {
console.log('getProgress calling');
this.getInfo();
this.setState({
currentTime: this.state.currentDuration,
});
}
<Slider
maximumValue={parseFloat(totalLength)}
minimumTrackTintColor="color"
maximumTractTintColor="color"
step={1}
value={currentTime}
onSlidingComplete={value => this.onValueChange(value)}
style={styles.slider}
thumbTintColor="red"
trackLength={parseFloat(totalLength)}
// onSlidingStart={() => this.setState({ paused: true })}
currentPosition={currentTime}
onProgress={e => this.getProgress(e)}
/>
It has to move slider value automatically according to audio duration
Any suggestions?
You'll need a counter to update the progress bar each second
timer = null;
durationCounter = () => {
this.timer = setInterval(async () => {
const info = await SoundPlayer.getInfo();
this.setState({
currentTime: info.currentTime
});
}, 1000);
};
componentDidMount = () => {
SoundPlayer.onFinishedLoading(async success => {
this.durationCounter();
});
}
componentWillMount = () => {
this.timer && clearInterval(this.timer);
};

react-native-setTimeout do not work well

I want to change the state by setTimeout
componentDidMount() {
this.timer = setTimeout(() => {
this.setState({
splashed: true
})
console.log('!');
}, 5000)
}
componentWillUnmount() {
console.log('nani');
this.timer && clearTimeout(this.timer);
}
but the setTimeout run soon when trigger componentDidMount , do not wait 5 seconds. how can i do it?
Thanks for your any suggestions.
Try to use another function for setting the state:
componentDidMount = () => {
this.timer = setTimeout(() => {
this.setSplashed();
console.log('!');
}, 5000)
}
setSplashed = () => {
this.setState({
splashed: true
})
}