I am building a react native app using expo.io.
The app is using a Stack Navigator to move between pages (cards).
My problem is that I have one page where users can create new items and I want to save the items when they leave the page.
Instead of saving all their changes, I want to prompt the user if they want to save changes before leaving the page so they get a chance to discard any changes they have made.
I have not been able to find an event for exiting the page that I can hook into and prompt the user if they want to save their changes?
The closest I have found to what I want to do is in backhandler, but that only works for Android back button.
Is there a way to do something similar if the user goes back with the back button in the header of the card, or if they use a swipe gesture?
Use NavigationEvents. Add event listeners to your components.
onWillFocus - event listener
onDidFocus - event listener
onWillBlur - event listener
onDidBlur - event listener
for example, the following will get fired when the next screen is focused.In the other screen, save user's changes in temporary storage, when they navigate back, get those unsaved changes and prompt the user, whether they want to save or not.
focusSubscription = null;
onWillFocus = (payload) => {
if (payload && payload.action && payload.action.type && payload.action.type === 'Navigation/BACK') {
// get values from storage here
// if there were unsaved changes prompt the user if they want to those changes or not
}
};
componentDidMount = () => {
this.focusSubscription = this.props.navigation.addListener(
'willFocus',
this.onWillFocus,
);
}
componentWillUnmount = () => {
this.focusSubscription && this.focusSubscription.remove();
this.focusSubscription = null;
};
Demo
React Navigation added in version 5.7 the beforeRemove event which you can use for this.
This is the demo from their site:
function EditText({ navigation }) {
const [text, setText] = React.useState('');
const hasUnsavedChanges = Boolean(text);
React.useEffect(
() =>
navigation.addListener('beforeRemove', (e) => {
if (!hasUnsavedChanges) {
// If we don't have unsaved changes, then we don't need to do anything
return;
}
// Prevent default behavior of leaving the screen
e.preventDefault();
// Prompt the user before leaving the screen
Alert.alert(
'Discard changes?',
'You have unsaved changes. Are you sure to discard them and leave the screen?',
[
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
{
text: 'Discard',
style: 'destructive',
// If the user confirmed, then we dispatch the action we blocked earlier
// This will continue the action that had triggered the removal of the screen
onPress: () => navigation.dispatch(e.data.action),
},
]
);
}),
[navigation, hasUnsavedChanges]
);
return (
<TextInput
value={text}
placeholder="Type something…"
onChangeText={setText}
/>
);
}
Related
I am trying to kick off some behavior within our application from a notification that is created using the Notification API. I doing this with some success, but we need the same behavior whether the user clicks the notification or closes the notification by clicking the X and for whatever reason the onclose event does not fire regardless of browser. This is a react application and the code looks something like this:
`async function showNotification() {
if (Notification.permission !== "granted") {
await Notification.requestPermission()
}
if (Notification.permission === "granted") {
const notification = new Notification('New Camera Alerts', {
body: 'There are new notifications to review in the Notification Panel',
icon: `${logo}`,
requireInteraction: true // this makes the dialog stay open until someone clicks it
})
const testEvent = (event: Event) => {
event.preventDefault()
// more lines of code here with what should happen when the
// notification is clicked/closed
}
notification.onclick = (event) => testEvent (event)
notification.onclose = (event) => testEvent (event)
}
}`
What I tried is assigning the same function call to both the click and close handlers. I was expecting both actions to have the same behavior. What actually happens is the close event is never fired at all.
I want a functional React component to close whenever the user clicks outside the application as well as outside the component within the application.
I've worked out how to close the component so long as the user clicks inside the containing application, but it still ignores clicks in an entirely different Windows application.
I want to emulate the behavior of the "Avatar" button at the top right of a Google Maps window. The button opens a modal dialog that is dismissed when the user clicks anywhere outside the dialog.
My current code has a useEffect hook that calls addEventListener and removeEventListener on the current document:
useEffect(() => {
const handleClickOutside = (event) => {
if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
onCloseButtonClick(event)
}
};
document.addEventListener("click", handleClickOutside, true);
return () => {
document.removeEventListener("click", handleClickOutside, true);
};
}, [onCloseButtonClick]);
I had hoped the solution was as simple as attaching addEventListener and removeEventListener on window instead of document, but it doesn't work.
How can I clear the visible property of the component when the user clicks outside the containing application?
Thanks in advance for your attention.
You probably need to also listen to onblur on the window. That can call the onCloseButtonClick straight away as theres no element attached to those events.
useEffect(() => {
const handleClickOutside = (event) => {
if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
onCloseButtonClick(event)
}
};
document.addEventListener("click", handleClickOutside, true);
window.addEventListener("blur", onCloseButtonClick, true);
return () => {
document.removeEventListener("click", handleClickOutside, true);
window.removeEventListener("blur", onCloseButtonClick, true);
};
}, [onCloseButtonClick]);
I have a form which I am auto saving by triggering a click every 1.5secs by targeting the button element and performing a .click:
let intervalID;
useEffect(() => {
if (editing) {
intervalID = setInterval(() => {
const formSubmitButton = document.querySelector(".btnaa");
formSubmitButton.click();
}, 1500);
This works well for the most part, but on modals etc it removes the focus and closes it.
How do I prevent this from happening? I tried using e.preventDefault() but then that does not save the form. Essentially what I am trying to do, is trigger the form to submit without removing focus.
I know many questions about this have already been asked but I'm struggling finding what I need. I am using react-router 5.1.2 to manage navigation in my web-app. I want to alert the user before he leaves the page. Attach beforeUnload listener does not work when users press the browser's back button. I understand I can use history.block() or <Prompt>, but the alert does not behaves like the default one shown when beforeUnload is fired, e.g. I can press back button twice then press "cancel" and have inconsistencies between the route and the actual rendered component. The question is: how can I alert the user pressing back button obtaining a confirmation dialog that behaves as the default one (preventing further actions other than those proposed by the alert)?
This is the custom hook I've used so far:
const alertUser = (e: BeforeUnloadEvent | PopStateEvent) => {
e.preventDefault();
e.returnValue = '';
};
export const useAlertBeforeLeaving = (showAlert = true) => {
const history = useHistory();
const unblock = useRef<UnregisterCallback | null>(null);
useEffect(() => {
if (showAlert) {
window.addEventListener('beforeunload', alertUser);
unblock.current = history.block('Leave the page?');
}
return () => {
if (showAlert && unblock.current) {
window.removeEventListener('beforeunload', alertUser);
unblock.current();
}
};
}, [history, showAlert]);
};
i have a question about how to reset field via action cancel and after click button add again the form will be reset.
this is my container
client.js
actionBatalAdd = (event) => {
event.preventDefault();
this.props.hideModalAdd()
this.props.resetField;
}
const mapStateToProps = state => ({
...state.locale,
...state.client,
layoutData: { ...state.layout },
resetField : reset("add-client"),
formValue: getFormValues('edit-client')(state),
formValue2 : getFormValues('add-client')(state),
});
i already try with reset from redux-form, but still no change, when i try to typing any character and then i press the cancel button, and after that i press the button add, the character still there, still no reset.