React Native Navigation, open universal link from within own app - reactjs

I am using React Navigation for React Native. I have successfully configured it to handle universal link, which is something like this
// linking.ts
import { APP_ID } from '#env';
const config = {
screens: {
LoginScreen: 'authorize',
RegisterScreen: 'register',
CustomerStack: {
screens: {
OrderDetailScreen: 'customer/order/:orderId',
},
},
},
};
const linking = {
prefixes: [`${APP_ID}://app/`, 'https://example.com/app/'],
config,
};
export default linking;
// App.tsx
import linking from './linking'
const App = () => {
return (
<NavigationContainer linking={linking}> <MyApp /> </NavigationContainer>
)
}
When I press a link in the browser such as https://example.com/app/customer/order/1234, then it successfully opens my app's order page.
Problem
I want to be able to open the url such as https://example.com/app/customer/order/1234 indside my app and have it open the order page. I have tried
<Button onPress={() => Linking.openURL('https://example.com/app/customer/order/1234')} />
but (testing on IOS) it switch to the web browser first to open the link, and then open my app.
Is it possible to open the order page directly inside my app without switching to the browser first.
Note: I am trying to implement an in-app notification history page, each notification item has the link saved in the database, and when the user clicks on the item I want to navigate the user to the page as configured in linking.ts. I know it is possible to parse the link and use navigation.navigate() instead, but that means I will have 2 places for the linking configuration. I think it would be great if I can reuse the existing logic provided by React Navigation.

In React Navigation, you can use the useLinkTo hook. This hook allows you to navigate inside the application using a path.
This will allow you to use the following option:
const linkTo = useLinkTo();
return (
<Button onPress={() => linkTo('/customer/order/1234')} />
);
If using a URL is mandatory, then you can use extractPathFromURL, an internal React Navigation function, to remove the prefix.
import extractPathFromURL from '#react-navigation/native/src/extractPathFromURL';
import linking from './linking'
// ...
const linkTo = useLinkTo();
return (
<Button onPress={() => {
const path = extractPathFromURL(linking.prefixes, 'https://example.com/app/customer/order/1234');
const pathWithSlash = path.startsWith('/') ? path : '/' + path;
linkTo(pathWithSlash);
} />
);
extractPathFromURL is not part of the official API and may be removed in future versions. For reliability, you can create a duplicate of this function in the project.

Related

Navigating From One local Host to another in react js

How can i navigate from one localHost:3000 to localHost:3001 in react Js
currently my auth is running in localHost:3000 and i want to navigate it to localHost:3001 when login successful
my current implementation is like
{agency && <Navigate to={"http://localhost:3001/"} replace />}
but it navigate me to http://localhost:3000/Login/localhost:3001/
but i want http://localhost:3001
Anyone?
react-router-dom only handles internal navigation actions, i.e. it can only link to and route within the React application. "http://localhost:3001/" is interpreted as an internal path and is appended to the current path.
If you need to navigate to an external (to the app) URL you should use raw anchor (<a />) tags or issue an imperative redirect via the window.location object.
Create a new navigation component to handle issuing the side-effect of external redirect in an useEffect hook.
Example:
const NavigateExternal = ({ to }) => {
useEffect(() => {
window.location.href = to;
}, []);
return null;
};
...
{agency && <NavigateExternal to="http://localhost:3001/" />}
You'd need to use
window.location.href = 'http://localhost:3001/'
Or using good practices
const redirectToExternalResource = url => window.location.href = URL
redirectToExternalResource('http://localhost:3001/')

How to use useNavigate previous page without accidentally leaving my site

Problem: I'm trying to use useNavigate(-1) to go to previous page, but I just implement a way to people arrive in my SPA with a pasted link (or social media tho), so, when user tries to go back they leave my site xP.
Process: I could use useNavigate("/"), but this makes my page scroll to the top and I want to keep the previous users position, I rather not use a state with my scroll position, but if nothing else is possible or simple...
Context: is a restaurant menu ifood-like page, this problems occurs in product page and I want to go back to product list.
What I'm trying to achieve: some way to check if previous page is out of my domain and not go there, replacing with my root.
ps. I'm using "react-router-dom": "^6.0.0-beta.0"
In earlier versions of React Router DOM, there was a smarter goBack method, but in modern versions (including the v6 beta), you must use the fact that the location.key is "default" for the initial entry to the app's history.
I created an npm package that encapsulates this functionality in a simple React hook.
Install use-back
npm install use-back
Usage
import {useBack} from "use-back";
const BackButton = () => {
const {hasBack, handleBack} = useBack();
return (
<button type="button" onClick={handleBack}>
{hasBack ? "Go Back" : "Go Home"}
</button>
);
};
CodeSandbox Demo
Source Code
The source code is basically the following, which you can also include in your application.
const MagicalBack = () => {
const navigate = useNavigate();
const location = useLocation();
const hasPreviousState = location.key !== "default";
const handleClick = () => {
if (hasPreviousState) {
navigate(-1);
} else {
navigate("/");
}
};
return (
<button type="button" onClick={handleClick}>
{hasPreviousState ? "Go Back" : "Go Home"}
</button>
);
};
CodeSandbox Demo

Window.location.reload() in react-native

I often use window.location.reload on my React Web project.
window.location.reload();
Is there any similar way to reload page(component) in react-native?
The thing that you mention is a browser feature. React Native uses native capabilities to render your app, so there are no browser features like window.location.reload()
So I am not sure what is your particular use-case for this. I guess it is needed to reload the current screen, not the full app.
In this case, you should use a react-way to re-render your screens. In React the views are re-rendered when the props or state changes. So if you want to trigger a full reload, you need to have some internal state that will trigger a reload. For example, you can use key property and a container with an internal state that toggles this key.
But I would consider it a hack. You should really use the react data-driven views
You can read more about key prop either in official docs or check out this article from Google: https://kentcdodds.com/blog/understanding-reacts-key-prop/
import React from 'react';
import { View, Text } from 'react-native';
const Container = () => {
const [key, setKey] = React.useState(0);
const reload = React.useCallback(() => setKey((prevKey) => prevKey + 1), []);
return <Child reload={reload} key={key} />;
}
const Child = ({ reload }) => {
const getRandomId = () => parseInt(Math.random() * 100, 10);
// We use useRef to showcase that the view is fully re-rendered. Use ref is initialized once per lifecycle of the React component
const id = React.useRef(getRandomId());
return (
<View>
<Text>My random id is {id}</Text>
<Button onPress={reload} />
</View>
)
}
#Coding is Life
import { NavigationEvents } from 'react-navigation';
<NavigationEvents onWillFocus={() => this.goBackReload()}/>
This is the way to reload the page, ie. when you go back to page you got the call back method. Using this method clear all state value and refresh the page. Another way is refresh control using to reload the app.
<ScrollView refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh} />
}>
</ScrollView>
When you scroll down the screen onRefresh method trigger.

Is there a way to Add new Route into React native Stack navigation dynamically

We are building an app in which are using React native stack Navigation,
Depends in the web api call based on the output we want to add the redirect to the same type of screen with different data and that new screen can create another screen..
We are using stack navigation with a single route at the start of the app.
Based on the Web Api call i want to inject a new Route to the existing stack and it should be able to go back to the previous screens
Is there Any way to achieve this.?
Quoted from https://reactnavigation.org/docs/en/navigating.html
If we call this.props.navigation.navigate with a route name that we haven't defined on a stack navigator, nothing will happen. Said another way, we can only navigate to routes that have been defined on our stack navigator — we cannot navigate to an arbitrary component.
I would suggest you to create all possible routes for app and according to web api just navigate app the what screen do you want with any data. It's all up to you.
According to React Navigation Documents, you can change your stack whenever you want:
index is the index of the route you want to navigate on new actions array of routes. (in this case we want to navigate to route 'NewRoute' and its index is 1)
import {StackActions, NavigationActions} from "react-navigation"
class Example extends Component {
// ... Some other methods
navigate = (id) => {
const resetAction = StackActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'Home' }),
NavigationActions.navigate({ routeName: 'NewRoute', params: { id, ...otherParams } }),
],
});
this.props.navigation.dispatch(resetAction);
}
render() {
return (
<View>
<TouchableOpacity onPress={() => this.navigate()}>
<Text>Go to New Route</Text>
</TouchableOpacity>
</View>
);
}
}

react-router redirect to a different domain url

I am using react-router for client side routing. I have a button and when some one clicks the button, I want to redirect the user to a different url.
For e.g I want to redirect the user to "http://www.google.com". I used navigation mixin and used this.transitionTo("https://www.google.com"). But when I do this I get this error
Invariant Violation: Cannot find a route named "https://www.google.com".
I can use window.location but is that the right way to go?
As pointed out in the comments to this answer, default way of solving this would be to use anchor element (the a tag) with href attribute that points at the destination URL that you'd like to route the user to. A button that has appearance of a button but behavior or an anchor is pretty much a web anti-pattern. See more info in this answer: https://stackoverflow.com/a/1667512/1460905.
That said, there certainly is a potential scenario when a web app needs to perform some action and only then redirect the user. In this case, if primary action the user takes is submitting some data or really performing an action, and redirect is more of a side-effect, then the original question is valid.
In this case, why not use location property of window object? It even provides a nice functional method to go to external location. See the ref.
So, if you have a component, say
class Button extends Component {
render() {
return (
<button onClick={this.handleClick.bind(this)} />
);
}
}
then add handleClick that would make the component look like
class Button extends Component {
handleClick() {
// do something meaningful, Promises, if/else, whatever, and then
window.location.assign('http://github.com');
}
render() {
return (
<button onClick={this.handleClick.bind(this)} />
);
}
}
No need to import window since it's global. Should work perfectly in any modern browser.
Also, if you have a component that is declared as a function, you may possibly use the effect hook to change location when state changes, like
const Button = () => {
const [clicked, setClicked] = useState(false);
useEffect(() => {
if (clicked) {
// do something meaningful, Promises, if/else, whatever, and then
window.location.assign('http://github.com');
}
});
return (
<button onClick={() => setClicked(true)}></button>
);
};
You don't need react-router for external links, you can use regular link elements (i.e. <a href="..."/>) just fine.
You only need react-router when you have internal navigation (i.e. from component to component) for which the browser's URL bar should make it look like your app is actually switching "real" URLs.
Edit because people seem to think you can't use an <a href="..." if you need to "do work first", an example of doing exactly that:
render() {
return <a href={settings.externalLocation} onClick={evt => this.leave(evt)}/>
}
async leave(evt) {
if (this.state.finalized) return;
evt.preventDefault();
// Do whatever you need to do, but do it quickly, meaning that if you need to do
// various things, do them all in parallel instead of running them one by one:
await Promise.all([
utils.doAllTheMetrics(),
user.logOutUser(),
store.cleanUp(),
somelib.whatever(),
]);
// done, let's leave.
this.setState({ finalized: true }), () => evt.target.click());
}
And that's it: when you click the link (that you styled to look like a button because that's what CSS is for) React checks if it can safely navigate away as a state check.
If it can, it lets that happen.
If it can't:
it prevents the navigation of occurring via preventDefault(),
does whatever work it needs to do, and then
marks itself as "it is safe to leave now", then retriggers the link.
You can try and create a link element and click it from code. This work for me
const navigateUrl = (url) => {
let element = document.createElement('a');
if(url.startsWith('http://') || url.startsWith('https://')){
element.href = url;
} else{
element.href = 'http://' + url;
}
element.click();
}
As pointed by #Mike 'Pomax' Kamermans, you can just use to navigate to external link.
I usually do it this way, with is-internal-link
import React from 'react'
import { Link as ReactRouterLink} from 'react-router-dom'
import { isInternalLink } from 'is-internal-link'
const Link = ({ children, to, activeClassName, ...other }) => {
if (isInternalLink(to)) {
return (
<ReactRouterLink to={to} activeClassName={activeClassName} {...other}>
{children}
</ReactRouterLink>
)
}
return (
<a href={to} target="_blank" {...other}>
{children}
</a>
)
}
export default Link
Disclaimer: I am the author of this is-internal-link
I had the same issue and my research into the issue uncovered that I could simply use an "a href" tag. If using target="_blank" you should write your link this...
Your Link
I couldn't find a simple way to do that with React Router. As #Mike wrote you should use anchor (<a> tags) when sending the user to external site.
I created a custom <Link> component to dynamically decide whether to render a React-Router <Link> or regular <a> tag.
import * as React from "react";
import {Link, LinkProps} from "react-router-dom";
const ReloadableLink = (props: LinkProps & { forceReload?: boolean }) => {
const {forceReload, ...linkProps} = props;
if (forceReload)
return <a {...linkProps} href={String(props.to)}/>;
else
return <Link {...linkProps}>
{props.children}
</Link>
};
export default ReloadableLink;

Resources