Refetch data when screen is focused - reactjs

Struggling with what I thought was a simple concept..
When a screen is focused, I want it to reload, unless I specify otherwise.
My screen is accessed from a bottom tab navigator, as well as other places in the app.
Using useFocusEffect, I can add this code which does trigger a reload:
const focusRefresh = useCallback(() => {
loadData();
}, [user?.userId, .. other deps here..]);
useFocusEffect(focusRefresh);
So I'm halfway there. However, some screens (such as a modal with more information), when they close and navigate back, I don't in this situation want another refresh. I'm struggling to get this. I can add some params when I go back from this modal:
navigator.navigate('Screen1',{dontRefresh:true})
But the issue here is that once I set this param, it seems to always be present. So after doing this, if I then navigate around via the bottom tabs, then I observe that the dontRefresh param is thereafter always present.
Can someone help?
Thanks!

Related

Confirmation on browser back button

I'm trying to achieve the following with Gatsby
The user is on a form page, if they hit the browser back button, a pop up would appear, asking if they want to leave.
If the user selects ok, then it would go back.
If the user selects cancel, then it would stay on this page
I was able to "almost" make it happen by doing the following
useEffect(() => {
const confirmExit = e => {
const leaveThisPage = window.confirm("Would you like to leave this page?")
if (!leaveThisPage) {
window.history.forward()
}
}
window.addEventListener("popstate", confirmExit)
return () => {
window.removeEventListener("popstate", confirmExit)
}
}, [])
There is one issue, if the user selects cancel, then the browser would go to the previous page. Then window.history.forward() would fire and sends them back.
I noticed that popstate event cannot be cancelled, so e.preventDefault() won't work.
Note: I also tried to test with window.onbeforeunload, but it only triggers if I close to window, or if my previous is from outside my app. Is there a work around for my issue?
Gatsby is using #reach/router under the hood, which doesn't support intercepting and preventing navigation (emphasis mine):
No history blocking. I found that the only use-case I had was preventing the user from navigating away from a half-filled out form. Not only is it pretty easy to just save the form state to session storage and bring it back when they return, but history blocking doesn’t happen when you navigate away from the app (say to another domain). This kept me from actually using history blocking and always opting to save the form state to session storage. (Source)
I second the recommendation for saving the form state using localStorage.

How to override Back button when a drawer is showing in React 16 / Router V4

I have a structure like this, there's a route like:
<Route path="/sample-route" component={ComponentA}/>
Then ComponentA has:
<ComponentA>
<ComponentB/>
<ComponentC>
<MaterialUIDrawer/>
</ComponentC>
</ComponentA>
ComponentC is used in 5 different routes as a child. The MaterialUIDrawer is showing based on a flag in a redux reducer. The problem I'm trying to solve is when the drawer is open, clicking back hides it but also navigates back. I tried solving it like this:
window.onpopstate = (e) => {
if (this.props.isOpen) {
this.props.toggleDrawer(false);
this.props.history.replace(this.props.match.url);
}
};
This has 2 issues:
If this is the first page you land on, it doesn't actually do anything, the function doesn't trigger
If you are on a different site and navigate to the url that contains the drawer, hitting Back moves you to a different URL (different domain)
I also tried setting a <Route/> in ComponentC and then the drawer lives there, but I didn't manage to get it to work, maybe the path is wrong. It felt like this might be the right way though, if path is /path1, then drawer lives in /path1/drawer, or /path2/drawer, etc.
I'm trying to find a way for the Back button in a browser to close the drawer (so execute a function I define) and override the default functionality.
I think you should be using props here instead of paths, so something like /path1?drawer=1, but you will definitely need to use history/location so that the back button can actually go back, so you are on the right trail.
I'm not sure what you are using for a browser history manager, but I would recommend tying off of that instead of leaning on the window pop-state. Your history module should be the source of truth and feed redux, not the other-way around IMO.

React route url and view changes but component function still active. Why?

I have an event function that is triggered by a button click. It's purpose is to keep the body position fixed keeping it from scrolling until you click the button again to close/toggle it off.
It works fine but, when I click to another page without toggling it off it is still active. Meaning the new page will not scroll because the body position is fixed.
I am new to React FYI
My code:
bodyFixed(event) {
document.body.classList.add('body-fixed');
}
bodyRelative(e) {
document.body.classList.remove('body-fixed');
}
I am using react-static withRouteData, RouteData, Router and I have no issues on those pages. But, on pages like an article page where the route doesn't change the same way. This is where I am seeing the problem.
Is there something I can wrap it with so that when I click to a new page it goes back to default?
Please first ask if you need more information and I will gladly add more.
Yes, you can call bodyRelative method in componentWillUnmount lifecycle hook to unset the class. Something along those lines:
componentWillUnmount() {
this.bodyRelative()
}

React-Router v4 - Prevent Transition With Function

I was able to prevent navigation as per the v4 docs, but I'm trying to hook up a function so that I can use a modal instead of an alert.
Function:
abandonForm = (route) => {
this.props.showModal('confirm');
console.log('leaving..');
}
In my page:
<NavigationPrompt when={true} message={(location) => this.abandonForm('confirm')} />
this.props.showModal('confirm') activates the modal successfully, but behind the modal the page still transitions - how can I prevent transition until a button in the modal is clicked?
Browsers only allow navigation cancellation by means of the alert box that you've mentioned. This restriction is motivated by phishing/scamming sites that try to use javascript gimmicks to create user experiences that convincingly mimic something that a browser or the OS would do (whom the user trusts). Even the format of the text shown in the alert box is crafted so that it's obvious that it originates from the site.
Of course, as long as the current URL stays within your app, you have control over it using react-router's history. For example you can do the following on navigation:
allow the navigation without confirmation
immediately navigate back to the previous location, but now with a modal on top
navigate away for real this time when the user clicks on a button in the modal.
The disadvantage of this approach (leaving out the sheer complexity of it) is that the user will not get a confirmation dialog if they try to navigate to a different site entirely.
Use:
this.unBlock = this.props.history.block((location, navigateToSelectedRoute) => {
// save navigateToSelectedRoute eg this.navigateToSelectedRoute =
// navigateToSelectedRoute;
// use this.navigateToSelectedRoute() afterwards to navigate to link
// show custom modal using setState
});
and when unblocking is done then call this.unBlock() to remove the listener.
Documentation here for history api

Alert user if the current page is temporarily left with React + React-Router

If a user attempts to change the active page (window/tab/etc) from our page to another while myState is true, we want to notify/alert the user in React JS using react-router.
I tried implementing it with the help TransitionHook and React-router's Confirming Navigation article. Though these only point out / work when user wants to close the page or reload it. Whereas I need to know when user just temporarily leaves the page without necessarily closing it.
How can I achieve this?
If by "leaving the page", you mean that the page is open, but a different window has popped up, you could consider using the document.hasFocus property. Here is one way I handled a problem that was tangentially related:
componentDidMount: function (){
setInterval(()=>{
if (document.hasFocus()){
this.checkServerState();
}
},
},

Resources