React Navigation AddListener Not working Correctly - reactjs

I am trying to deepLinking and catching the url when url open the page on the screen which is functionally works when my app is not working on the background. However, it doesn't work if app is working on the background.
const isFocused = useIsFocused();
useEffect(() => {
getCode();
}, [isFocused]);
const getCode = async () => {
//we will generate a button in the forget password email, link will include a url ===> mobile://auth/new-password?verification=534396
const url = await Linking.getInitialURL();
console.log('url', url);
if (url?.includes('new-password')) {
//problem, it may not work if app is still working on the background
const query = queryString.parseUrl(url);
const verifyCode = query.query.verification;
setVerificationCode(String(verifyCode));
setIsLoading(false);
} else {
Alert.alert('Something went wrong');
}
};
When I directlinked to application with the link, it console log as "url null". Is my problem on the focusing part or on the getInitialUrl function?

I was experiencing a similar issue.
In my case we used linking from NavigationContainer and it would open the same X screen left on background regardless if the deeplink data had a different value for that screen.
I fixed it by using the getId on Stack.Screen:
const getId = ({ params }) => params?.id;
<Stack.Screen name="X" component={XComponent} getId={getId} />
You can find more info on getId here https://reactnavigation.org/docs/screen/#getid.

Related

How to reset channel of window.echo on changing of route reactjs

I am using laravel-websockets to listen to event. I have no issue on the back-end side; The issue is on the front-end side.
SCENARIO:
When I go to a specific route post/[slug], the current channel is based on the current slug. When I redirect to the same route but different value of slug, the channel listens to the first value on page refresh and not to the current one.
const Component = () => {
const router = useRouter();
const {slug} = router.query;
useEffect(() => {
window.Echo.private(`post.${slug}`).listen('PrivateEvent', e => {
console.log(e)
});
}, [slug])
}
Example:
On page refresh, go to post/first-slug. Next, click to <Link to="/post/second-slug">About</Link>
The example above should listen to second-slug and not the first-slug.
How can I solve this without hard refresh or <a> tag?
You forgot to stop listening on the previous channel, so the events are still received. I suppose that you end up with two active channels, receiving events for both.
Inside a useEffect() you should return a cleanup function that clears resources created for the effect
Here is how:
const Component = () => {
const router = useRouter();
const {slug} = router.query;
useEffect(() => {
window.Echo.private(`post.${slug}`).listen('PrivateEvent', e => {
console.log(e)
});
return () => window.Echo.private(`post.${slug}`).stopListening('PrivateEvent');
}, [slug])
}
If this does not solve your problem, please:
display the slug in your component (return <div>slug</div>;) to confirm that the navigation really happens ;
show us the whole console log.

Prompt user when url changes using react-router-dom

Assuming I am within an edit form page that has the url route of /person/edit/123
Within my react app, I also have a website logo at the top left of my app that when clicked, returns the user to the url route /home
Using react-router-dom v6 or some other means, I need to be able to check that when a user is within an edit page and decides to click on the website logo, I need to prompt the user that changes have been made and provide some message that has a "Leave page yes/no dialog"
Unsure what approach to take inorder to accomplish the above.
I have seen other threads within SO but they are using older versions of react-router-dom.
Any guidance would be great.
UPDATE: Code used but didn't seem to work:
useEffect(() => {
window.addEventListener('beforeunload', function (e) {
var confirmationMessage =
'It looks like you have been editing something. ' +
'If you leave before saving, your changes will be lost.';
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
}, []);
This demostrate a simplified version of a complex example to manually check for dirty forms, instead of relying on unload.
Considering you have a mechanism to check for dirty forms.
e.g.
const Component = ({ text }) => {
const [ edited, setEdited ] = useState(text)
const checkDirty = () => edited !== text
return (... my form codes here...)
}
One of the solution is to create a CustomLink component. (psuedo code)
const CustomLink = React.forwardRef(({ onClick: dirty, href }, ref) => (
const beforeHref = (e) => {
e.preventDefault();
if (typeof dirty == "function") {
if (!dirty()) {
return redirect(href)
} else {
if (confirm("Should we redirect!")) {
return redirect(href);
} else {
return null
}
}
}
redirect(href);
}
// you should probably use the link component here
return <a href={href} onClick={beforeHref} {...rest} />
));
Then in the page you can create a link like
<CustomLink href="/somepage" onClick={checkDirty} />
PS: Of course in the overall pages, you must pass those props to your menu, and your logo.

How to setup a function which gets app settings and sets it as localStorage before the page loads. (next.js)

I've been working on a Next.JS web application for the past couple of days but I've reached a problem. The app has an API call (/api/settings) which returns some settings about the application from the database. Currently, I have a function which returns these settings and access to the first component:
App.getInitialProps = async () => {
const settingsRequest = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/settings`
);
const settingsResponse = await settingsRequest.json();
return { settings: settingsResponse };
};
This does work and I am able to pass in settings to components but there are two problems with this:
I need to nest the prop through many components to reach the components that I need
This request runs every time a page is reloaded/changed
Essentially, I need to create a system that does this:
runs a function in the _app.tsx getInitialProps to check if the data is already in localStorage, if not make the API request and update localStorage
have the localStorage value accessible from a custom hook.
Right now the problem with this is that I do not have access to localStorage from the app.tsx getInitialProps. So if anyone has an alternative to run this function before any of the page loads, please let me know.
Thanks!
I found a solution, it might be a janky solution but I managed to get it working and it might be useful for people trying to achieve something similar:
First we need to create a "manager" for the settings:
export const checkIfSettingsArePresent = () => {
const settings = localStorage.getItem("app_settings");
if (settings) return true;
return false;
};
export const getDataAndUpdateLocalStorage = async () => {
const r = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/settings`);
const response = await r.json();
localStorage.setItem("app_settings", JSON.stringify(response));
};
With that created we can add a UseEffect hook combined with a useState hook that runs our function.
const [doneFirst, setDoneFirst] = useState<boolean>(false);
useEffect(() => {
const settingsPreset = checkIfSettingsArePresent();
if (performance.navigation.type != 1)
if (settingsPreset) return setDoneFirst(true);
const getData = async () => {
await getDataAndUpdateLocalStorage();
setDoneFirst(true);
};
getData();
}, []);
//any other logic
if (!doneFirst) {
return null;
}
The final if statement makes sure to not run anything else before the function.
Now, whenever you hot-reload the page, you will see that the localStorage app_settings is updated/created with the values from the API.
However, to access this more simply from other parts of the app, I created a hook:
import { SettingsType } from "#sharex-server/common";
export default function useSettings() {
const settings = localStorage.getItem("app_settings") || {
name: "ShareX Media Server",
};
//#ts-ignore
return JSON.parse(settings) as SettingsType;
}
Now I can import useSettings from any function and have access to my settings.

React-Native deeplinking if app is closed doesn't work ( React-Navigation v5)

I have implemented deep links for my app with React Navigation V5.
I have a problem regarding Deep Linking. If the app is closed(killed) and it is opened via a deep link it will take me to the home screen rather than the screen it has to take me to.
Here's my linking config, from what I've read in the docs (here), i'm passing the URL from the getInitialUrl function to the subscribe and here
const onReceiveURL = ({ url }) => listener(url);
it should parse the URL to a valid navigation state and take me to the screen it has to. I might be mistaken with how subscribe works, though.
Any help is appreciated, thanks in advance!
const linking = {
prefixes: ['appName://', 'app.appName.com://', APP_WEB_DOMAIN],
async getInitialURL() {
// Check if app was opened from a deep link
const url = await Linking.getInitialURL();
if (url != null) {
return url;
}
},
subscribe(listener) {
const onReceiveURL = ({ url }) => listener(url);
Linking.addEventListener('url', onReceiveURL);
return () => {
// Clean up the event listener
Linking.removeEventListener('url', onReceiveURL);
};
},
config: {
screens: {
SignInScreen: 'login',
UnauthenticatedStack: '',
TrackListScreen: 'playlist/:id/' //:id gets parsed as a string, you have to specify it if you want a number.
}
}
};
I had a similar issue (deep link from push notification) due to a bug in react-native-splash-screen
have a look here https://github.com/spencercarli/react-native-splash-screen-demo/pull/11

Reactjs SPA - Pressing IOS Safari back button updates the url but not the component view

We have a reactjs SPA application where we want to navigate back and forth using browser back button/swipe the screen, if on Mac.
when a user clicks on a link from home page, they will be navigated to a detail page and the url looks like https://example.com/pdetail?prdt=Computer
On detail page, User has an ability to search, and when the user searches(Say Stationery), we update the url and push the url to history and the the detail page component is updated with the searched data.Like
https://example.com/pdetail?prdt=Stationery
filterSearch(searchval) {
//search related code to set variables
let newUrl = setParams(searchval, 'prdt')
this.props.history.push(`?${newUrl}`);
// dispatch an api call to get data
}
export function setParams(query = "", param) {
const searchParams = new URLSearchParams(window.location.search);
searchParams.set(param, query.trim());
return searchParams.toString();
}
Now when the user browse back using the browser back button, we expect it to go the previous data(https://example.com/pdetail?prdt=Computer) from (https://example.com/pdetail?prdt=Stationery) and Vice Versa as we move back and forth. It is working fine in chrome and IE.
But in Safari, as the user presses back button, url is changing in the browser but the component view is not.
as we navigate back, I noticed that it did not go to ComponentDidMount as we go to the same page. But it goes ComponentDidUpdate so I added my code in there.
componentDidUpdate() {
window.onpopstate = (e) => {
if(this.props.history.action == 'POP')
{
e.preventDefault();
const search = window.location.search;
const params = new URLSearchParams(search);
if(params.has('prdt') === true)
{
const selectedprdt = params.get('prdt');
this.props.fetchDetails('FETCH_PRDT_DETAILS' , selectedprdt);
}
}
}
}
Let me know how can get this back forth page navigation with consistent url and data in Safari.
It is working fine in IE and chrome.
EDIT:
I have edited my code to add an eventListener in componentDidMount and UI is working in Safari. But when I see in the logs, I noticed the event runs multiple times not just once, which is very inefficient as we are making server calls.
componentDidMount()
{
const selectedprdt = getParams(window.location, 'prdt');
if(selectedprdt !== '')
{
this.props.fetchDetails('FETCH_PRDT_DETAILS' , selectedprdt);
}
window.addEventListener('popstate', this.handleBackButton)
}
handleBackButton = (e) =>
{
if(this.props.history.action == 'POP')
{
const search = window.location.search;
const params = new URLSearchParams(search);
if(params.has('prdt') === true)
{
const selectedMarket = params.get('prdt');
this.props.fetchDetails('FETCH_PRDT_DETAILS' , selectedprdt);
}
e.preventDefault();
}
}
Please let me know how to stop eventListener to executing the calls multiple times.
Thanks,
I had to remove the same listener on component Unmount.
componentWillUnMount()
{
window.removeEventListener('popstate', this.handleBackButton)
}

Resources