I have a form which is written using formik and react-hooks. I am trying get recommendations on how to track a form change when user clicks home button or page refresh/reload?
I have tried, browser history method to trigger a modal to show unsaved changes
const unlisten = browserHistory.listen( location => {
//trigger modal here
});
But this seems to doesn't work. Also I am using react-router...if any suggestions using that will be helpful.
I am all done storing formvalues into a state using useState and setState,
I have a modal to show when exit button is clicked on form to track form changes but I am not sure how to detect outside of form.
I will put this here as a posible workaround to the question.
When you update your form in your handleChange method (whatever it is),you can also update user localstorage. With this approach you can check when page loads if there is some values in storage from older sessions or before.
Setting Values:
this.setState({ item1: value }, () => {
localStorage.setItem('myForm', JSON.stringify(this.state));
});
Getting values:
componentDidMount() {
const storedValues = JSON.parse(localStorage.getItem('myForm'))
this.setState({...storedValues});
}
Restore Data:
In your onSubmit method remember to clear localStorage
localStorage.removeItem('myForm');
I'm using react-router-navigation-prompt for exactly that purpose, it's made for react router. Just add this component inside your <Form/> / <form/>,
<NavigationPrompt
when={formProps.dirty}>
{({ isActive, onConfirm, onCancel }) => {
if (isActive) {
return (
<ConfirmationPopUp
isOpen
onConfirm={onConfirm}
onCancel={onCancel}
/>
);
}
}}
</NavigationPrompt>
Related
I am showing user a modal that takes over the entire screen if user is accessing the website on phone. A user tends to instinctively click the back button on their phone to go back to the previous screen instead of using the close button provided on the modal.
Is there a way to intercept the function that is triggered when a user clicks the back button? I want to close the modal when user clicks the back button on their phone, instead of having them redirect to the previous page.
Use the History API. An example on how to acomplish this is:
//listen for state changes
window.onpopstate = (event) =>
{
if (!event.state.modalOpened)
{
closeModal()
}
}
//change the actual page state so it contains the modalOpened property
window.history.replaceState({modalOpened: false})
function openModal(content)
{
//push new state, put the modal information in the state object, this will push a new state, when the user presses the back button, the browser will just trigger the onpopstate event, instead of going to the previous page
window.history.replaceState({modalOpened: true})
//this is any code you use to open your modal, for example
ReactDOM.render(<Modal>{content}</Modal>, document.getElementById("modal-container")
}
class based component
There are many ways to aproach this, this is one of them, everything you need to make something that fits with your app is in the History API DOCS.
//listen for state changes
window.onpopstate = (event) =>
{
if (!event.state.modalOpened)
{
updateModal(false, null)
}
}
function openModal()
{
//push new state, put the modal information in the state object, this will push a new state, when the user presses the back button, the browser will just trigger the onpopstate event, instead of going to the previous page
window.history.replaceState({modalOpened: true})
updateModal(false, <div>Modal content!</div>)
}
function updateModal(open, content)
{
ReactDOM.render(<Modal open={open} content={content} />, document.getElementById("modal-container")
}
//change the actual page state so it contains the modalOpened property
window.history.replaceState({modalOpened: false})
class Modal extends React.Component {
constructor(props) {
super(props);
}
render() {
//check the history to determine if we have to open or close the modal
return <div className={"modal " + (props.open)? "show" : ""}><div className="modal-content">{props.content}</div><button onClick={() => window.history.back()}>OK</button></div>;
}
}
I have a button and when we press it, Firebase updates some value in Firebase. How can I control users? When we press it, Firebase updates some value in Firebase. How can I control users' presses? And allow the user to press this button only once?
My idea is that when opening some page in useEffect, I check if this user pressed a button earlier (I store all users' IDs to check if a user pressed a button) and if there is no user ID, then I allow the user to press the button, but if there is a user ID, then I disable the button and the user can't press it again.
But here is some problems. When user open page while firebase checks if there is user button is available for pressing. This is problem and I dont' want to allow do that.
And also if previosly user didn't press the button and now wants to do that then I need to imidiatelly disable it to prevent pressing it more than once.
How To disable button while data is fetched from firebase (on page loading)
How To disable button after 'click'
Here's a test component which accomplishes what you are asking:
import React, {useEffect, useState} from 'react';
import { Button } from 'react-native-paper';
const Test = props => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
useEffect(() => {
const fetchMyAPI = async () => {
setIsButtonDisabled(true) // Disable button
let response = await fetch('api/data')
response = await response.json()
// Do whatever you want with response
setIsButtonDisabled(false) // Enable button after fetching
}
fetchMyAPI()
}, [])
const onPressHandler = () => {
setIsButtonDisabled(true)
// Button functionality can go here
}
return (
<Button onPress={onPressHandler} disabled={isButtonDisabled}></Button>
);
};
export default Test;
This is for react native so just replace the react native Button with whatever button you want to use.
Edit:
If you are having a problem with the button disabled state when reloading this component it is likely due to the component re-rendering. You will have to lift the state up to the parent and pass the state back down to the children. This will avoid the state being reset when the button gets re-rendered.
See the react docs https://reactjs.org/docs/lifting-state-up.html
I know this question is asked alot... I just have a specific scenario which isn't working.
I'm using Ant design AutoComplete component. For the onSelect I parse various values and then do a this.props.history.push(url) which works perfectly fine. The route is picked up and the view changes
One of the other actions I want to perform though is that when someone types into the autocomplete field and hit the enter key, it should go to my search page. The url does change, but the page doesnt redirect. If I hit F5 to refresh the page, it does open on the correct component.
<AutoComplete
dropdownClassName="search-dropdown"
dataSource={optionsB}
size={this.props.size}
onSelect={this.onSelectOption}
onSearch={this.props.handleSearchDashboard}
labelInValue
optionLabelProp="option"
className="searchArea"
defaultActiveFirstOption={false}
>
<Input
ref={input => {
this.searchBar = input;
}}
onPressEnter={e => this.goTo(e)}
/>
</AutoComplete>
And here is the goTo():
goTo = e => {
e.preventDefault();
e.stopPropagation();
const url = `/search/${e.target.value}`;
console.log("url", url);
this.props.history.push(url);
};
Any suggestions as to what might be happening here?
Our application is developed using react and on refreshing and route navigation, we need to check for unsaved changes in the page and show an alert.
For this scenario, we were able to leverage with route API on page-navigation,
and yet refreshing the page directly seems not working.
So in your case basically a user has typed a long form, so there are below cases you need to show him the message that unsaved changes will be lost.
When switching to different component clicking on any of the link
Refreshing the component
So inorder to do these there is a native event called beforeunload, so you need to add the event to the document by using document.addEventListener in componentDidMount and you can unmount that one in componentWillUnmount in the parent component
so there should be check to know the form has been dirty which means user has typed something, like isDirty in redux-form.
so whenever the user is typing something you can make this value to true based on this value if the user clicks on the refresh you can have the check and execute it
You can check more on here and this
so you can hold a state variable at the parent level and based on this you can trigger it
You can use Promise struct and run your refresh other whatever if you want in then
export const unSavedChanges= (values,listTitle)=> new Promise((resolve, reject) => {
//Your code of check, validation what ever
})
when you call this func
unSavedChanges(changes,"list").then(response=>{
refresh.page
}
)
In my web app there is a form where users put in data. If they want to navigate to other component or back, I want to notify them that changes were made to form and show them confirmation dialog.
I know how to implement this if users click on back button on top of the form, but don't have any idea how to handle it when users click on sidebar menu options. (there is a sidebar with multiple links to other components - using react-router).
I have tried something using componentWillunmount() but no luck since component gets unmounted in any case.
If anyone has experience in this I would really need some help.
Try the routerWillLeave hook, here is a example from react-router's documentation:
const Home = withRouter(
React.createClass({
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
},
routerWillLeave(nextLocation) {
// return false to prevent a transition w/o prompting the user,
// or return a string to allow the user to decide:
if (!this.state.isSaved)
return 'Your work is not saved! Are you sure you want to leave?'
},
// ...
})
)
https://github.com/ReactTraining/react-router/blob/master/docs/guides/ConfirmingNavigation.md