How to make a modal toggle only when navigating from a specific page in React Native? - javascript

I have two screens in my app called "ReadyScreen" and "RunningScreen."
The user can access ReadyScreen on one of two ways:
1: When they are getting ready to start a run (when they click the start run button)
2: When they finish the run, they will be navigated from RunningScreen back to the ReadyScreen.
When the user finishes a run, i want a modal to toggle showing them:
How Many Steps They Ran, Average HR, etc.
but I only want it to toggle when they are navigating from "RunningScreen."
Would this require an "if" statement basically stating if they navigated from RunningScreen the modal will toggle, else, it will not? Or is there more nuance to this?

i would send a route param in RunningScreen:
navigation.navigate('ReadyScreen', {
didComeFromRunningScreen: true
})
then in RunningScreen get the variable:
const RunningScreen = ({ route }) => {
const { didComeFromRunningScreen } = route.params
useEffect(() => {
if(didComeFromRunningScreen){
//show modal
}
}, [])
}
i believe this should work.

Related

How to work with multiple tabs using react-idle-timer

I am having a react Application, where I need to display a modal with two buttons (Logout and Continue Session) when the user goes idle, It was actually working fine with the IdleTimer component from the react-idle-timer.
But If I open the Application in multiple tabs, and click on Continue session in one tab, the other tabs are not receiving that, The sample code that I have used is,
<IdleTimer
timeout={1000}
onIdle={// Opens a modal with two buttons Logout and Continue Session}
></IdleTimer>
Is it possible to use the crossTab prop? but I am not sure how to implement them.
How can I achieve my requirement to work with multiple tabs so that if I click on continue session all the tabs need to close the modal.
The version I am using is - react-idle-timer (4.6.4)
Could someone please help with this to achieve my requirement? Thanks in advance!!
You can use the Cross Tab Messaging feature from the documentation:
// Action dispatcher (redux)
const dispatch = useDispatch()
// Message handler
const onMessage = data => {
switch (data.action) {
case 'LOGOUT_USER':
dispatch(logoutAction())
break
// More actions
default:
// no op
}
}
// IdleTimer instance
const { message } = useIdleTimer({ onMessage })
// Logout button click
const onLogoutClick = () => {
// Tell all tabs to log the user out.
// Passing true as a second parameter will
// also emit the event in this tab.
message({ action: 'LOGOUT_USER' }, true)
}
See the documentation for v5, here.

Next.js behavior on back button pressed

I have a page I am trying to fix in order to keep scroll position when user presses back button (browser). Let's say I have a component called list, where I show the user some products. To see all the products the user can scroll down the list component. When the user clicks on some product, the application redirects the user to the detail component. Then when the user tries to go back to the list, hits the back button of the browser, the list component gets rendered and it seems like it scrolls to top automatically.
As far as I know, pressing the back button of the browser triggers a window.history.back() action, nothing else happens.
For a solution, I have implemented a variable in the context of my application that saves the scrollY value and then, in the componentWillMount (or useEffect) of the component I am trying to render (list component), I set the scroll position to the value set in the context.
Details of my solution are here, as I have based my entire code in this stack overflow's post:
How to change scroll behavior while going back in next js?
I have checked the value using some logs and the scroll position is saved correctly in the context, however, as I am using a window event listener, it sets the value to zero just after the list component is rendered.
In my code I am not using any kind of scroll configuration, so I was wondering if that behavior is some sort of default for either Next.js or react. It happens when the user hits the back button of the browser, but I am a newbie to next and I don't know if I am missing something or what, I don't even know if this issue has something to do with React or Next.js itself.
This gist may be of assistance as it includes a custom hook to manage scroll position: https://gist.github.com/claus/992a5596d6532ac91b24abe24e10ae81
import { useEffect } from 'react';
import Router from 'next/router';
function saveScrollPos(url) {
const scrollPos = { x: window.scrollX, y: window.scrollY };
sessionStorage.setItem(url, JSON.stringify(scrollPos));
}
function restoreScrollPos(url) {
const scrollPos = JSON.parse(sessionStorage.getItem(url));
if (scrollPos) {
window.scrollTo(scrollPos.x, scrollPos.y);
}
}
export default function useScrollRestoration(router) {
useEffect(() => {
if ('scrollRestoration' in window.history) {
let shouldScrollRestore = false;
window.history.scrollRestoration = 'manual';
restoreScrollPos(router.asPath);
const onBeforeUnload = event => {
saveScrollPos(router.asPath);
delete event['returnValue'];
};
const onRouteChangeStart = () => {
saveScrollPos(router.asPath);
};
const onRouteChangeComplete = url => {
if (shouldScrollRestore) {
shouldScrollRestore = false;
restoreScrollPos(url);
}
};
window.addEventListener('beforeunload', onBeforeUnload);
Router.events.on('routeChangeStart', onRouteChangeStart);
Router.events.on('routeChangeComplete', onRouteChangeComplete);
Router.beforePopState(() => {
shouldScrollRestore = true;
return true;
});
return () => {
window.removeEventListener('beforeunload', onBeforeUnload);
Router.events.off('routeChangeStart', onRouteChangeStart);
Router.events.off('routeChangeComplete', onRouteChangeComplete);
Router.beforePopState(() => true);
};
}
}, [router]);
}
Looking at your url, using shallow routing could solve the problem. Where the URL will get updated. And the page won't get replaced, only the state of the route is changed. So you can change your logic according to that.
A good example is in the official documentation:
https://nextjs.org/docs/routing/shallow-routing
And you might use display: 'hidden' to hide and show your components conditionally according to your state!
It's a way around but it could be even more useful depending on your exact situation !
After looking for another solution that does not use the window.scroll and similar methods, I have found a solution.
1st solution (worked, but for me that I have an infinite list that is loaded via API call, sometimes the window.scroll method wasn't accurate): I take the window.scrollY value and set it in the session storage, I did this before leaving the list page, so in the details page, if user hits the back button, at the moment the page is loading, I get the Y value from session storage and use the window.scroll method to force the page to scroll to the previously configured value.
As I mentioned earlier, this worked, but in my case, I have a list that is populated from an async API call, so sometimes the page loaded without all the images and the scroll was already configured, then the images and data were loaded and the user ended up seeing some other place in the page rather than the desire position.
2nd solution: In my case we are talking about a e commerce app, so I found this solution useful as it focuses in a particular item with its corresponding ID instead of the Y coord of the window. Scroll Restoration in e commerce app

react-native navigation false route params in navigation listener

I am using react native navigation and have two tabs. One of them has a list of entites, the other has a form to submit entities. When ever a new entity is submitted, I'd like to refresh the first tab to be sure, the newest data gets loaded.
I navigate to the first tab like this:
navigation?.navigate('Main', {refresh: true});
This props gets saved as follows:
let {refresh} = route.params;
To check, if the screen needs to refresh, I added a listener in useEffect() like this:
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () =>{
if(refresh){
doRefresh()
}
})
}, [])
Unfortunately, neither "refresh" nor "route.params" directly is ever true inside the listener. Passing the param works, because I took a closer look at the var and it gets true, whenever I submit.
Do I need to access the route.params inside a navigation listener in another way?
Thanks and hit me up, if you need more information on this
The issue is that your useEffect only runs once as your dependencies array [] is empty, and therefore your listener only receives the initial value of refresh.
You need to pass refresh as a dependency to useEffect, and I don't think you even need the focus listener in your case so you would end up with this:
useEffect(() => {
if (refresh) {
doRefresh()
}
}, [refresh])
const onPressCountry = (item,index) => {
navigation.push('SignUp2', {selectedCountryId:item?.countryID, selectedCountryName: item?.name} )
}
use navigation.push instead of navigation.navigate

Want to have an event handler for the browser's back button with next.js

I am having a modal which while opening pushes a hash to url example.com/#modal, on browser back button click, I want to recognise that event so that I can toggle the state of modal. the point is, since Im using it with next.js (server side rendering), I will not be having access to window object (correct me if I am wrong). so I need an alternate way to handle the event of browser back button.
You can use next/router's beforePopState to act on changes to the session history navigation (back/forward actions), and make sure it'll only happen when leaving the current page.
useEffect(() => {
router.beforePopState(({ as }) => {
if (as !== router.asPath) {
// Will run when leaving the current page; on back/forward actions
// Add your logic here, like toggling the modal state
}
return true;
});
return () => {
router.beforePopState(() => true);
};
}, [router]); // Add any state variables to dependencies array if needed.
#Summy I hope your issue is resolved by now. If not you can try this for the browser back button:-
Next.js + React Go back to the previous page
If you want to use hashbang URLs you can't use SSR, since /# and /#/one is the same route server-side, so there is no way for the server to know what to render, it will need to send a basic template and let the client fill it, in that case, I think using CRA or Parcel with React Router and its HashRouter is a better option, that way you will have a single index.html and let the client decide what to render.
in NextJs we can use beforePopState function and do what we want such close modal or show a modal or check the back address and decide what to do
https://stackoverflow.com/a/60702584/4717739
https://stackoverflow.com/a/69560739/4717739

Use Prompt from React Router to block history.push

I'm using React-Router's Prompt component to block a user from switching tabs when a form is in a dirty state. Each tab has its own route and I'm using history.push to update the URL. Prompt shows up as expected when the form is dirty, but the user is still taken to the new URL when they click "cancel".
Here is the function that handles switching tabs and updating the URL.
const handleClick = (tabId, disabled, route) => {
if (disabled) {
return false;
}
setSelectedIndex(tabId);
if (route) {
history.push(route);
}
return true;
};
I'm guessing that history.push gets executed not matter what.
Every example I see of using Prompt shows it being used with Link. Is there a way to get Prompt to work with history.push?

Categories