React Native - BackHandler with Alert - javascript

I'm attempting to override the back button on a screen with a prompt to Logout. See the following:
import React, { Component } from "react";
import { Alert, BackHandler } from "react-native";
export default class Dashboard extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress",this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.handleBackPress);
}
handleBackPress() {
Alert.alert(
"Logout",
"Are you sure you want to logout?",
[
{
text: "Cancel",
onPress: () => {
console.log("Cancel Pressed");
},
style: "cancel"
},
{ text: "Logout", onPress: () => this.handleLogout() }
],
{ cancelable: false }
);
}
handleLogout() {
this.props.navigation.navigate("Login");
}
}
As you can see, on mounting change, I'm binding and unbinding "hardwareBackPress" to this.handleBackPress. Note, I have to use .bind(this), otherwise I get
_this2.handleLogout is not a function
when I press the Logout in the Alert. Expected functionality is:
Back button is pressed
Functionality disabled (doesn't navigate)
Alert is displayed
Ok is pressed
Navigates back
Back button on previous screen has default action
But what actually happens is:
Back button is pressed
Navigates back
Alert is displayed
(Subsequent steps don't matter)
I noticed I don't have return true; anywhere in my handleBackPress(), so I added that:
handleBackPress() {
Alert.alert(
"Logout",
"Are you sure you want to logout?",
[
{
text: "Cancel",
onPress: () => {
console.log("Cancel Pressed");
},
style: "cancel"
},
{
text: "Logout",
onPress: () => {
return this.handleLogout();
}
}
],
{ cancelable: false }
);
return true;
}
But what happens now is:
Back button is pressed
Functionality disabled (doesn't navigate)
Alert is displayed
Ok is pressed
Navigates back
Back button press on previous screen results in Alert being displayed
I have verified that componentDidUnmount() is called, but it doesn't seem to have removed the event listener.
Has anyone encountered this issue before? For now, I've just resorted to globally disabling the back button by adding this handler at the entry point of the app, but that's not a long-term solution.
Edit: I noticed there's a lifecycle alternative, so I tried implementing that:
componentDidMount() {
this.backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
Alert.alert("Logout", "Are you sure you want to logout?", [{ text: "Cancel", onPress: () => {}, style: "cancel" }, { text: "Logout", onPress: () => this.handleLogout() }], { cancelable: false });
return true;
});
}
componentWillUnmount() {
this.backHandler.remove();
}
And while this makes it work (somehow), it has yet another side-effect. As soon as I navigate forward (which doesn't trigger componentDidUnmount(), due to stacked navigation), and navigate back, the back button behaves as such:
Back button is pressed
Functionality disabled (doesn't navigate)
Alert doesn't show up
The screen I'm navigating forward to has it's back button overridden, and seems to not play well with this alternative lifecycle. Will attempt to implement different approach on all subsequent screens; see if it behaves properly then.

Using the lifecycle alternative from the Documentation (https://facebook.github.io/react-native/docs/backhandler) seems to handle odd behaviour with Alert and return true;:
Dashboard.js
componentDidMount() {
this.backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
Alert.alert("Logout", "Are you sure you want to logout?", [{ text: "Cancel", onPress: () => {}, style: "cancel" }, { text: "Logout", onPress: () => this.handleLogout() }], { cancelable: false });
return true;
});
}
componentWillUnmount() {
this.backHandler.remove();
}
handleLogout() {
global.screenName = "Dashboard";
return this.props.navigation.navigate("Login");
}
As long as all subsequent screens that need the back button overridden also use the same logic:
Detail.js (subsequent screen in Stack navigator):
componentDidMount() {
this.backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
return this.props.navigation.navigate("Dashboard");
});
}
componentWillUnmount() {
this.backHandler.remove();
}

Related

How do I edit the task in my to do list by doing a long press?

I am trying to create an edit function for updating a task that was previously written.
I have tried this so far but apparently the prompt is only for the browser. Would this even work? What are alternatives to create the prompt for react native?
const taskUpdate = (index) => {
const newItemsCopy = [...taskItems];
const item = newItemsCopy[index];
let newItem = prompt(`Update ${item.task}?`, item.task);
let todoObj = { todo: newItem, complete: false };
newItemsCopy.splice(index, 1, todoObj);
if (newItem === null || newItem === "") {
return;
} else {
item.task = newItem;
}
setTaskItems(newTodoItems);
}
Full Code
You can implement this using Modal
On long press, open the Modal which consists of textInput with value.
Edit the value.
Save the value on close/ handle it with save button inside Modal.
You can use Alert from react-native.
import { Alert } from "react-native";
Alert.alert(
"Alert Title",
"My Alert Msg",
[
{
text: "Cancel",
onPress: () => console.log("Cancel Pressed"),
style: "cancel"
},
{ text: "OK", onPress: () => console.log("OK Pressed") }
]
);

Not able to perform 2 actions on the same component when hardware back button is pressed in react-native,below are the code regarding the backhandler

backAction = () => {
if (this.props.navigation.isFocused() && !this.state.otpScreen) {
this.setState({ showLoginScreen: true });
return true;
} else if(this.state.showLoginScreen) {
this.props.navigation.dispatch(CommonActions.reset({
index: 0,
routes: [
{ name: 'Login' },
],
}))
return false;
}
};
the code above is for action that should be done by the hardware back press, at first it should show the one screen and on the second press it should exit the app, both otp screen and login screen are on the same component, I'm just hiding based on the condition ...
at the first time it is working as per the condition but for the second time it's not.
componentDidMount() {
this.focusListener = this.props.navigation.addListener('focus', () => {
this.setState({
mobile: '',
showLoginScreen: true,
});
});
this.getDataFromStorage();
AsyncStorage.setItem('countryCode', '+91');
BackHandler.addEventListener(
"hardwareBackPress",
this.backAction
);
}
componentWillUnmount() {
clearInterval(this.interval)
BackHandler.addEventListener('hardwareBackPress', () => { return false })
}
can anyone pls help me how to do this,thanks in Advance
I guess your problem is that you can't access the 'current' value of a state inside a listener.
More Details here
Try to use a Reference instead of a state

Prevent going back when hardware back button is pressed in Ionic 4 App

this.platform.backButton.subscribe(()=> {
const alert = await this.alertController.create({
header: 'Confirm!',
message: 'Do you want to go back!!!',
buttons: [
{
text: 'Yes',
handler: () => {
// Previous page loaded
}
}, {
text: 'No',
handler: () => {
//Page should not go back.
//This is where i want to write code,if the user clicks
No and the back button function should be disabled.
//Only when the user presses Yes,the page will go to
previous.
}
}
]
});
})
I dont know how to handle when the user presses no,i.e.Disable the back button function or event.
Finally i solved the issue.As the event emitted from the backButton is an promise.If I dont need to go back,i just reject that promise.
this.platform.backButton.subscribe(()=> {
const alert = await this.alertController.create({
header: 'Confirm!',
message: 'Do you want to go back!!!',
buttons: [
{
text: 'Yes',
handler: () => {
// Previous page loaded
}
}, {
text: 'No',
handler: () => {
reject()
}
}
]
});
})
Try this way to prevent the back button
this.platform.backButton.subscribeWithPriority(9999, () => {
this.dismiss();
});

Javascript - prevent navigation during file upload

I have a vue component for video upload, where I am warning a user when he tries to navigate away during the video upload that he will lose the file if he does so, like this:
ready() {
window.onbeforeunload = () => {
if (this.uploading && !this.uploadingComplete && !this.failed) {
this.confirm('Are you sure you want to navigate away? Your video won't be uploaded if you do so!');
}
}
}
I am using sweetalert to alert the user about it. But how can I then make it stay on the same page, and prevent the navigation away before he confirms that he wants to navigate away?
This is the whole component:
<script>
function initialState (){
return {
uid: null,
uploading: false,
uploadingComplete: false,
failed: false,
title: null,
link: null,
description: null,
visibility: 'private',
saveStatus: null,
fileProgress: 0
}
}
export default {
data: function (){
return initialState();
},
methods: {
fileInputChange() {
this.uploading = true;
this.failed = false;
this.file = document.getElementById('video').files[0];
this.store().then(() => {
var form = new FormData();
form.append('video', this.file);
form.append('uid', this.uid);
this.$http.post('/upload', form, {
progress: (e) => {
if (e.lengthComputable) {
this.updateProgress(e)
}
}
}).then(() => {
this.uploadingComplete = true
}, () => {
this.failed = true
});
}, () => {
this.failed = true
})
},
store() {
return this.$http.post('/videos', {
title: this.title,
description: this.description,
visibility: this.visibility,
extension: this.file.name.split('.').pop()
}).then((response) => {
this.uid = response.json().data.uid;
});
},
update() {
this.saveStatus = 'Saving changes.';
return this.$http.put('/videos/' + this.uid, {
link: this.link,
title: this.title,
description: this.description,
visibility: this.visibility
}).then((response) => {
this.saveStatus = 'Changes saved.';
setTimeout(() => {
this.saveStatus = null
}, 3000)
}, () => {
this.saveStatus = 'Failed to save changes.';
});
},
updateProgress(e) {
e.percent = (e.loaded / e.total) * 100;
this.fileProgress = e.percent;
},
confirm(message) {
swal({
title: message,
text: null,
type: "warning",
showCancelButton: true,
cancelButtonText: "Cancel",
cancelButtonColor: '#FFF',
confirmButtonColor: "#2E112D",
confirmButtonText: "Yes, delete"
}).then(function(){
this.$data = initialState();
}.bind(this), function(dismiss) {
// dismiss can be 'overlay', 'cancel', 'close', 'esc', 'timer'
if (dismiss === 'cancel') { // you might also handle 'close' or 'timer' if you used those
// ignore
} else {
throw dismiss;
}
})
}
},
ready() {
window.onbeforeunload = () => {
if (this.uploading && !this.uploadingComplete && !this.failed) {
this.confirm('Are you sure you want to navigate away? Your video won't be uploaded if you do so!');
}
}
}
}
</script>
Mozilla documentation suggests
window.onbeforeunload = function(e) {
var dialogText = 'Dialog text here';
e.returnValue = dialogText;
return dialogText;
};
and also states that:
Since 25 May 2011, the HTML5 specification states that calls to window.alert(), window.confirm(), and window.prompt() methods may be ignored during this event. See the HTML5 specification for more details.
Source contains many other details regarding reasons and what to expect from modern browsers.
This question seems to be a duplicate of yours.
This answer suggests that to avoid weird browser behaviour you should set handler only when it's to prevent something (that is while navigating away should trigger a confirmation dialog)
But how can I then make it stay on the same page, and prevent the navigation away before he confirms that he wants to navigate away?
Add return false; to stop the event.
if (this.uploading && !this.uploadingComplete && !this.failed) {
this.confirm("Are you sure you want to navigate away? Your video won't be uploaded if you do so!");
return false; // <==== add this
}
return false; does 3 separate things when you call it :
event.preventDefault(); – It stops the browsers default behaviour.
event.stopPropagation(); – It prevents the event from propagating (or “bubbling up”) the DOM.
Stops callback execution and returns immediately when called.

Dialog cancel button needs to stop browser event

I am trying to display a dialog/confirm when a user browses away from a page/component.
I have the following dialog component which currently fires using a listener:
emptyBasket: function () {
basketChannel.publish({
channel: "basket",
topic: "emptyBasket",
data: {
}
});
},
OnClick which fires publish:
onTabLinkClick: function(e) {
var url = window.location.href;
var currentRoute = url.substr(url.length - 6)
if(this.state.BasketTotal > 0 && currentRoute != 'basket' ){
basketChannel.publish({
channel: "basket",
topic: "openDialog",
data: {
dialogTitle: 'Warning',
dialogContent: 'You have contacts in your basket, are you sure you want to leave?'
}
});
}
}
The listener in my dialog component then changes the state and displays the dialog:
_listenerForDialog: function(data) {
this.setState({
title: data.dialogTitle,
content: data.dialogContent,
visible:true
});
},
This works, but I would like to add an ok and cancel button, if the cancel button is clicked I would like stop the event.
How could I attach my component to e.preventDefault()?
Can I make this work like the native browser confirm()?
var confirmDialog = confirm("You have contacts in your basket, are you sure you want to leave?");
if(confirmDialog == false ){
e.preventDefault();
} else{
this.emptyBasket();
}
I am using the npm package https://www.npmjs.com/package/rc-dialog
UPDATED
onClose in current component:
onClose(){
this.setState({
visible:false
});
basketChannel.publish({
channel: "basket",
topic: "emptyBasket",
data: {
}
});
},
Thanks in advance

Categories

Resources