React Navigation dynamic screens and deep linking - react-native

I want to create dynamically tab screens with react navigation, but I don't know how to figure it out with the deep linking.
I would like to have my tabs screens accessible with deeplinking like : /tabs/:tabId but I don't know how to deal with the linking config.
If there is someone who can help me, I have created this snack :
https://snack.expo.dev/#natinho68/getting-started-%7C-react-navigation

First you need to set it up via documentation
This is minimal example. Root stack is a stack navigator which has 3 screens: Splash, Login and App. And App is another stack navigator(nested) with 2 screens: Dashboard and Login.
const deepLinkDevPrefix = "https://custom.server.org"
const deepLinkLocalProtocol = "customprotocol://"
const deepLinkConfig = {
screens: {
Splash: "Splash",
Login: "Login",
App: {
path: "App",
screens: {
Dashboard: "Dashboard",
Profile: "Profile,
},
},
}
}
Inside of getInitialURL you can handle url that opened the app.
const App = () => {
const getInitialURL = async () => {
// Check if app was opened from a deep link
const url = await Linking.getInitialURL();
console.log('This is url')
console.log(url)
if (url !== null) {
return url;
}
return undefined
}
const linking = {
prefixes: [deepLinkDevPrefix, deepLinkLocalProtocol],
deepLinkConfig,
getInitialURL
};
return (
<NavigationContainer linking={linking}>
<RootStack />
</NavigationContainer>
);
}
Here you can find out how to navigate without navigation prop.

Related

React Navigation: Navigating outside a component

I am trying to navigate my app from outside of a component. Specifically, I am using a fetch interceptor and I want to navigate whenever an error response is received.
I followed the example here: https://reactnavigation.org/docs/navigating-without-navigation-prop/
However, my app is still giving me an error saying that either a navigator isn't rendered or the navigator hasn't finished mounting:
Screenshot of app with error message
As far as I can tell, neither of those situations apply. The app is loaded and rendered with a navigator in place before I try to actually navigate
My App.jsx:
// ... imports and so on ...
fetchIntercept.register({
response: (response) => {
if (response.status === 401) {
// Unverified subscription
RootNavigation.reset({ index: 0, routes: [{ name: 'Intercept' }] });
}
return response;
},
});
{ ... }
const InterceptNavigator = createStackNavigator(
{
Application: {
screen: ApplicationScreen,
},
Intercept: {
screen: SubscriptionInterceptScreen,
},
},
{
initialRouteKey: 'Application',
},
);
const App = createAppContainer(InterceptNavigator);
export default () => {
React.useEffect(() => {
RootNavigation.isMountedRef.current = true;
return () => { RootNavigation.isMountedRef.current = false; };
}, []);
return (
<NavigationContainer ref={RootNavigation.navigationRef}>
<App />
</NavigationContainer>
);
};
RootNavigation.js:
import * as React from 'react';
export const isMountedRef = React.createRef();
export const navigationRef = React.createRef();
export function navigate(name, params) {
if (isMountedRef.current && navigationRef.current) {
navigationRef.current.navigate(name, params);
}
}
export function reset(options) {
if (isMountedRef.current && navigationRef.current) {
navigationRef.current.reset(options);
}
}
I also inserted a number of console logs throughout and all of them showed that the app is loaded, that the navigationRef is current, and that the isMountedRef is also current before the app tries to navigate
Try .resetRoot() instead of .reset(). I think .reset() needs a state as an argument.
Found the solution. The issue is that I had a mixture of version 4 and version 5 code (and was referring to mixed documentation).
To fix the issue I removed references to version 5 code and then followed the steps on this page to get the navigator working: https://reactnavigation.org/docs/4.x/navigating-without-navigation-prop/

React Navigation Navigate from Firebase Push Notification

I can't figure out how to navigate from push notifications from firebase fcm. I was having issues with navigation being undefined so I followed this guide, but it still doesn't change screens. Is there some basic step I am missing. I have tried to navigate to several other screens without params and none navigate, and I am not getting any errors:
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
Here is my navigation service:
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
const { data } = notificationOpen.notification;
NavigationService.navigate("Chat", {
chatName: `${data.channelName}`,
chatId: `${data.channelId}`
});
});
And here is my main App file:
import SplashScreen from 'react-native-splash-screen';
const TopLevelNavigator = createStackNavigator({
ChatApp
},
{
headerMode: 'none',
navigationOptions: {
headerVisible: false,
}
});
const AppContainer = createAppContainer(TopLevelNavigator);
class App extends Component {
async componentDidMount() {
SplashScreen.hide();
}
render() {
return (
<Provider store={store}>
<AppContainer ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}} />
</Provider>)
}
}
export default App;
The issue was that I needed to have the other stack inside of the same navigator. Once I added that it started working
const TopLevelNavigator = createStackNavigator({
ChatApp: {
screen: ChatApp,
},
Chat: {
screen: ChatScreen,
}
});

How to navigate on specific screen from route directory in react native?

I am using react switch navigator from react-navigation library. I create a separate file router.js and call in my App.js. Now i am using react-native-firebase and from the app.js i need to navigate to specific screen. If anyone having an idea then please let me know here is my code:
App.js:
import Router from "./src/Router";
class App extends Component<Props> {
render() {
return (
<Router />
);
}
}
export default App;
Router.js
const AppSwitchNavigator = createSwitchNavigator({
InitialScreen: {
screen: InitialScreen
},
Login: {
screen: Login
},
Forgot: {
screen: Forgot
},
SuccessMessage: {
screen: SuccessMessage
},
App: AppStackDelivery,
AppR: AppStackRecipient,
}, {
initialRouteName: 'InitialScreen'
});
const Router = createAppContainer(AppSwitchNavigator);
export default Router;
From the push notification i am receiving id and i need to navigate to detail screen but i don't get anything to my router or initial screen please anyone having an idea then please share.
You should use the Deep link. And go to the path you want in onNotification.
Example
handleNotification () {
const path = "chat/Eric";
const prefix = Platform.OS === 'android'
? 'myapp://myapp/'
: 'myapp://'
const url = `${prefix}${path}`;
Linking.openURL(url).catch(err => console.error(err));
firebase.messaging().setBadgeNumber(0);
}

Basic deep linking not working with React Navigation and Redux

EDIT:
I am wondering if this issue is related to the fact that I have the redux store wrapping the appContainer. Is there another way I am supposed to handle deep linking when using Redux?
I cannot get my deep links to work. They open up the app but do not direct to any page. I have followed directions exactly as is listed here https://reactnavigation.org/docs/en/bottom-tab-navigator.html
I am not sure if it is because of the createSwitchNavigator. I can't figure out how to troubleshoot to figure this out either.
const AppSwitchNavigator = createSwitchNavigator({
Open: { screen: AuthStackNavigator },
Home: {
screen: MainStackNavigator,
path: ''
}
})
const AppContainer = createAppContainer(AppSwitchNavigator)
const PREFIX = 'myApp://';
const App = () => {
return (
<Provider store={store}>
<AppContainer
uriPrefix={PREFIX}
/>
</Provider>
)
}
In MainStackNavigator I have this...
const MainStackNavigator = createStackNavigator(
{
MainTabNavigator: {
screen: MainTabNavigator,
path: ''
},
Contact: {
screen: Contact,
path: 'contact',
},
...
But when i go to myApp://contact - it opens the app but doesn't go the contact screen, and of course, any of the screens in the TabNavigator don't get anywhere either.

How to redirect to login screen in react navigation tab navigator with redux

I'm trying to figure out how to go about coding this.
I'm using a React Navigation TabNavigator as my main navigator and am also using Redux to manage my app and user's auth state.
One of the tabs has content that can only be displayed if the user is logged in so what I am trying to do is when they press that tab, if they are not logged in yet, I want to redirect to or pop a modal on top with a login/registration screen.
After they have successfully logged in and their content pulled down, then I want to show the screen in the tab that they were originally trying to view.
So how would you go about this?
From my understanding with the TabNavigator, after the initial load then componentWillMount does not run each time I click on that tab so I can't check my auth state there and react.
Is there a way to intercept the tab press otherwise and be able to check my auth state from there before loading the view for that tab?
First, you need to activate lazy options on TabNavigator config, eg:
const AppNavigator = TabNavigator(
{
Home: { screen: HomeScreen },
LoginScreen: { screen: LoginScreen },
/* the screen needed auth */
AddPost: {screen: AddPostScreen},
...
},
{
lazy: true,
...
})
Second, I add this package react-navigation-is-focused-hoc
$ yarn add react-navigation-is-focused-hoc
It's for the checking active screen, on react render AppNavigator add some props:
...
import { updateFocus } from 'react-navigation-is-focused-hoc';
...
return (
...
<AppNavigator
onNavigationStateChange={(prevState, currentState) => {
updateFocus(currentState);
}}
/>
...
);
The last, add isFocused to your Authenticated screen (AddPostScreen):
import { withNavigationFocus } from 'react-navigation-is-focused-hoc';
...
#withNavigationFocus('AddPostScreen')
class AddPostScreen extends React.Component {
static navigationOptions = () => ({
/* Your navigation options */
})
componentWillReceiveProps(nextProps) {
const { isFocused, auth: { signedIn }, navigation: { navigate } } = this.props;
if (!isFocused && nextProps.isFocused && !signedIn) {
navigate('LoginScreen');
}
}
shouldComponentUpdate(nextProps) {
const { isFocused } = this.props;
if (isFocused && !nextProps.isFocused) {
return true;
}
// Don't update if the screen is not focused
if (!isFocused && !nextProps.isFocused) {
return false;
}
// Update the screen if its re-enter
return !isFocused && nextProps.isFocused;
}
render() {
return (
/* return authenticated component */
...
signedIn (boolean) is state from your auth reducers