Deeplink with nested DrawerNavigator, TabNavigator and StackNavigator - react-native

I'm trying to configure deeplinks for my React Native app, following the official documentation, but I haven't been able to make it work. I mean, the app does open if I run adb shell am start -W -a android.intent.action.VIEW -d “crf://" packageName or xcrun simctl openurl booted crf:// but I haven't been able to open a specific screen; it always launches to the app home screen. I'm almost sure it has to do with the nested navigators, since I have a TabbarNavigator inside a DrawerNavigator and a StackNavigator inside all of that. I followed the instructions regarding nested navigators in the documentation and also this post, amongst other things, but no luck. I want to go to the EventHomeScreen, PersonDetailScreen and ProgramSessionDetail
Here is my code:
NavigatorRouter
const mainNavigator = createStackNavigator(
{ 
[Constants.APP_HOME_SCENE_KEY]:{
screen: AppHomeScreen,
navigationOptions: {
title: 'App Home',
showBack: false,
showSearch: false,
},
path: ''
},
[Constants.EVENT_HOME_SCENE_KEY]:{
screen: navPropMapper()(EventHomeScreen),
navigationOptions:{
title: 'Home',
path: 'event'
}
},
[Constants.ATTENDEE_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(PersonDetailScreen),
navigationOptions:{
title: 'Attendee Detail',
// path: 'person/:user'
path: 'person'
}
},
[Constants.PROGRAM_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(ProgramSessionDetail),
navigationOptions:{
title: 'Program',
path: 'program/:idLecture'
}
}
},
{
initialRouteName: `${Constants.APP_HOME_SCENE_KEY}`,
defaultNavigationOptions: {
header: props => <NavBar {...props} />,
gesturesEnabled: false,
showBack: true,
showHome: false,
showSearch: true,
showWebExplorer: false
}
});
const tabbarNavigator = createBottomTabNavigator({
Main: {
screen: mainNavigator,
path: 'tabs'
},
}, {
tabBarComponent: Tabbar,
});
const drawerNavigator = createDrawerNavigator({
Drawer: {
screen: tabbarNavigator,
navigationOptions:{
drawerLockMode: 'locked-closed',
},
path: 'drawer'
},
}, {
contentComponent: ({ props }) => <DrawerContainer {...props}/>,
drawerPosition: 'right',
unmountInactiveRoutes: true,
defaultNavigationOptions: {
header: null,
}
});
const wraperNavigator = createStackNavigator({
MainComponents: {
screen: drawerNavigator,
path: 'main'
},
[Constants.MODAL_FEEDBACK]:{
screen: navPropMapper()(Modal),
navigationOptions:{
title: 'Feedback',
}
},
[Constants.MODAL_LOGIN]:{
screen: navPropMapper()(ModalLogin),
navigationOptions:{
title: 'Login',
}
},
}, {
mode: 'modal',
cardStyle:{
backgroundColor: 'transparent',
opacity: 1,
},
cardShadowEnabled: true,
cardOverlayEnabled: true,
transparentCard: true,
defaultNavigationOptions: {
header: null,
gesturesEnabled: false,
},
});
export default createAppContainer(wraperNavigator)
App.js
class App extends Component {
render () {
return (
<Provider store={store}>
<RootContainer />
</Provider>
)
}
}
export default App
RootContainer
class CRrootContainer extends Component {
render () {
return (
<View style={styles.applicationView}>
<NavigationRouter uriPrefix={'crf://'}/>
</View>
)
}
}
const mapStateToProps = (state) => {
return {
navState: state.navigation,
}
}
// wraps redux integration
const mapDispatchToProps = (dispatch) => ({
startup: () => dispatch(StartupActions.startup()),
})
export default connect(mapStateToProps, mapDispatchToProps)(rootContainer)
iOS Uri and Scheme
AppDelegate.m
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [RCTLinkingManager application:application openURL:url
sourceApplication:sourceApplication annotation:annotation];
}
URL Types:
Android Scheme:
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="packageName">
<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:roundIcon="#mipmap/ic_launcher_round"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="crf"/>
</intent-filter>
</activity>
</application>
</manifest>
The commands I'm trying are:
xcrun simctl openurl booted crf://main/drawer/tabs/event for iOS.
and
adb shell am start -W -a android.intent.action.VIEW -d “crf://main/drawer/tabs/event" packageName for Android (packageName is the name of my app package`
My react-navigation version is 3.6.1 and react-native: 0.59.9.

can you try setting an event listener ?
class CRrootContainer extends Component {
async componentDidMount(){
if (Platform.OS === "android") {
let url = await Linking.getInitialURL();
if(url) this._setDeepLink(url);
} else {
Linking.addEventListener("url", this._handleOpenURL);
}
};
_handleOpenURL = event => {
this._setDeepLink(event.url);
};
_setDeepLink = url => {
const { navigate } = this.props.navigation;
const route = url.replace(/.*?:\/\//g, "");
const routeName = route.split("/")[0];
console.log("root====>", routeName);
if (routeName === "foo") {
navigate("fooRoute"); // to navigate route based on deep link url
}
};
componentWillUnmount() {
Linking.removeEventListener("url");
}
render () {
return (
<View style={styles.applicationView}>
<NavigationRouter uriPrefix={'crf://'}/>
</View>
)
}
}
const mapStateToProps = (state) => {
return {
navState: state.navigation,
}
}
// wraps redux integration
const mapDispatchToProps = (dispatch) => ({
startup: () => dispatch(StartupActions.startup()),
})
export default connect(mapStateToProps, mapDispatchToProps)(rootContainer)

So it ended up beeing a rookie mistake, putting the path: keyword on the wrong place; it needs to go outside the navigationOptions, not inside. Also, path value on drawer navigator and tabbar navigator should be empty.
const mainNavigator = createStackNavigator(
{
[Constants.APP_HOME_SCENE_KEY]:{
screen: AppHomeScreen,
navigationOptions: {
title: 'App Home',
showBack: false,
showSearch: false,
},
path: ''
},
[Constants.EVENT_HOME_SCENE_KEY]:{
screen: navPropMapper()(EventHomeScreen),
navigationOptions:{
title: 'Home',
},
path: 'event'
},
[Constants.ATTENDEE_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(PersonDetailScreen),
navigationOptions:{
title: 'Attendee Detail'
},
path: 'person'
},
[Constants.PROGRAM_DETAIL_SCENE_KEY]:{
screen: navPropMapper()(ProgramSessionDetail),
navigationOptions:{
title: 'Program'
},
path: 'program/:idLecture'
}
},
{
initialRouteName: `${Constants.APP_HOME_SCENE_KEY}`,
defaultNavigationOptions: {
header: props => <NavBar {...props} />,
gesturesEnabled: false,
showBack: true,
showHome: false,
showSearch: true,
showWebExplorer: false
}
});
const tabbarNavigator = createBottomTabNavigator({
Main: {
screen: mainNavigator,
path: ''
},
}, {
tabBarComponent: Tabbar,
});
const drawerNavigator = createDrawerNavigator({
Drawer: {
screen: tabbarNavigator,
navigationOptions:{
drawerLockMode: 'locked-closed',
},
path: ''
},
}, {
contentComponent: ({ props }) => <DrawerContainer {...props}/>,
drawerPosition: 'right',
unmountInactiveRoutes: true,
defaultNavigationOptions: {
header: null,
}
});
const wraperNavigator = createStackNavigator({
MainComponents: {
screen: drawerNavigator,
path: ''
},
[Constants.MODAL_FEEDBACK]:{
screen: navPropMapper()(Modal),
navigationOptions:{
title: 'Feedback',
}
},
[Constants.MODAL_LOGIN]:{
screen: navPropMapper()(ModalLogin),
navigationOptions:{
title: 'Login',
}
},
}, {
mode: 'modal',
cardStyle:{
backgroundColor: 'transparent',
opacity: 1,
},
cardShadowEnabled: true,
cardOverlayEnabled: true,
transparentCard: true,
defaultNavigationOptions: {
header: null,
gesturesEnabled: false,
},
});
export default createAppContainer(wraperNavigator)

Related

Make react native navigator go back to previous screen

I have a tab navigator (with 3 screens) then each of these have also 2 or 3 screen each.
TabNavigator => "A,B,C"
A stack => "1,2,3"
Sometimes I'llgo from 1 to 2, and sometimes from B to 2 (for example), I want that when pressing the back button navigates to the previous screens so 1 for first example and B on the seconds. But right now only goes to 1.
Here is my navigation stack:
const RaidsStack = createStackNavigator({
Raids: { screen: RaidsScreen, },
CreateRaid: { screen: CreateRaidScreen, },
})
const ChatsStack = createStackNavigator({
Chats: { screen: ChatsScreen },
ChatRoom: { screen: ChatRoomScreen, },
})
const SettingsStack = createStackNavigator({
Settings: { screen: SettingsScreen },
EditProfile: { screen: editProfileScreen },
Start: { screen: StartScreen },
})
const StartStack = createStackNavigator({
Tabs: { screen: SettingsScreen },
Register: { screen: editProfileScreen },
Start: { screen: StartScreen },
})
const TabNavigator = createBottomTabNavigator(
{
Raids: { screen: RaidsStack },
Chats: { screen: ChatsStack },
Settings: { screen: SettingsStack }
},
{
tabBarPosition: "bottom",
tabBarComponent: props => {
return (
<Footer>
<FooterTab>
<Button
vertical
active={props.navigation.state.index === 0}
onPress={() => props.navigation.replace('Tabs', {}, NavigationActions.navigate({ routeName: 'RaidsStack' }))}>
<Icon name="egg" />
<Text>Raids</Text>
</Button>
<Button
vertical
active={props.navigation.state.index === 1}
onPress={() => props.navigation.replace('Tabs', {}, NavigationActions.navigate({ routeName: 'Chats' }))}>
<Icon name="mail" />
<Text>Chats</Text>
</Button>
<Button
vertical
active={props.navigation.state.index === 2}
onPress={() => props.navigation.replace('Tabs', {}, NavigationActions.navigate({ routeName: 'Settings' }))}>
<Icon name="settings" />
<Text>Settings</Text>
</Button>
</FooterTab>
</Footer>
);
}
}
)
const AppStack = createStackNavigator({
Start: { screen: StartScreen },
Register: { screen: RegisterScreen },
Tabs: TabNavigator,
Raids: { screen: RaidsScreen },
Chats: { screen: ChatsScreen },
Settings: { screen: SettingsScreen }
}, {
headerMode: 'none',
})
How to do it? I'm new to react and react navigation.
I suggest looking at the documentation https://reactnavigation.org/docs/stack-navigator/
Since you also using nested navigator I suggest looking at this page too https://reactnavigation.org/docs/nesting-navigators/
It’s exactly tells shows you the example of nested navigator with a tab
Hope this material will help you!

navigation.push not adding a new route

navigate works fine however when I replace it with push, it stops working.
<Text
onPress={() => {
//works
this.props.navigation.navigate("VideoPlayer", { id });
// doesn't work
this.props.navigation.push("VideoPlayer", { id });
}}
style={styles.text}
>
{title}
</Text>
How can I get push to work so I can remount components?
I am using a Drawer with a new stack for each route so I can use headers:
const config = {
initialRouteName: "Home",
contentOptions: {
activeTintColor: "#e91e63",
itemStyle: {
flexDirection: "row-reverse"
}
},
drawerWidth: 300,
drawerPosition: "right"
};
const withHeader = (
screen: Function,
routeName: string,
Header
): StackNavigator =>
createStackNavigator(
{
[routeName]: {
screen,
navigationOptions: ({ routeName, props }) => ({
header: props => <Header {...props} />
})
}
},
{
transparentCard: true
}
);
const routes = {
Home: {
screen: withHeader(HomeScreen, "Home", BasicHeader)
},
Links: {
screen: withHeader(LinksScreen, "Links", DrawerHeader)
},
Settings: {
screen: withHeader(SettingsScreen, "Settings", DrawerHeader)
},
VideoEpisodes: {
screen: withHeader(VideoEpisodesScreen, "Video Episodes", DrawerHeader)
},
VideoPlayer: {
screen: withHeader(VideoPlayerScreen, "Video Player", DrawerHeader)
},
TestYourself: {
screen: withHeader(TestYourselfScreen, "Test Yourself", DrawerHeader)
},
MyResults: {
screen: withHeader(MyResultsScreen, "My Results", DrawerHeader)
},
BookmarkedVideos: {
screen: withHeader(
BookmarkedVideosScreen,
"Bookmarked Videos",
DrawerHeader
)
},
Search: {
screen: withHeader(SearchScreen, "Search", DrawerHeader)
},
About: {
screen: withHeader(AboutScreen, "About", DrawerHeader)
}
};
const AppNavigator = createDrawerNavigator(routes, config);
export default createAppContainer(AppNavigator);
.push() is a StackAction method, you have to use a stackNavigator in order to use it

I have a problem in my code when updating react-navigation v2 to v3

I want to update the react-navigation library V2 to V3 and change part of my code thinking that there would not be any problems but it turns out that I have problems creating createStackNavigator with a screen of type createDrawerNavigator and that in turn contains createBottomTabNavigator.
my code that works with the previous version was:
export const createRootNavigator = (signedIn = false) => {
const commonNavigationOptions = {
headerStyle: {
shadowColor: 'transparent',
elevation: 0
},
headerTintColor: DEFAULT_THEME.topaz
};
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer('Home'),
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.backgrouncolorHomeSafeArea)
})
},
Cards: {
screen: Tabs('Cards'),
navigationOptions: () => ({
headerStyle: {
height: 0
}
})
},
);
const SignedOut = createStackNavigator(
{
SignIn: {
screen: LoginContainer,
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.dark)
})
},
SelectableCardsList: { screen: SelectableCardsListComponent },
);
return createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
);
};
const Drawer = (initialRoute) => createDrawerNavigator(
{
Home: { screen: Tabs('Home') },
{
initialRouteName: initialRoute,
contentComponent: CustomDrawerComponent
}
);
const Tabs = (initialRouteName) => createBottomTabNavigator(
{
Home: {
screen: HomeContainer,
navigationOptions: {
tabBarLabel: I18n.t('tabs.me')
}
},
Home2: {
screen: Home2,
navigationOptions: {
tabBarLabel: I18n.t('tabs.credentials')
}
},
{
initialRouteName: initialRouteName,
tabBarComponent: ({ navigation }) => <CustomBottomBarComponent navigation={navigation} navigationState={navigation['state']} />,
tabBarOptions: {
style: {
backgroundColor: 'white'
}
}
}
);
try this solution with react-navigation V3 and send me an error
I try the following:
encapsulate createSwitchNavigator in createAppContainer and separate (SignedOut and SignedOut) createStackNavigator out of createSwitchNavigator, the rest is still the same.
export const createRootNavigator = (signedIn = false) => createAppContainer(createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
));
I get the following error: The component for route 'Home' must be a react component. For Example
import MyScreen from './MyScreen';
...
Home : MyScreen,
}
you can also use a navigator:
The problem is located in this part:
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer,
Also try to change Drawer for any component (a component with a blank screen) and this works, but I can not insert the Tabs in the Drawer.
Thank you very much for your help.

React-navigation drawer is routing me back to previous screen immediately after rendering item screen

I have a StackNavigation like this:
const AppNavigator = createStackNavigator({
Login: {
screen: Login,
navigationOptions: () => ({
title: 'Login',
headerTintColor: 'white',
headerStyle:{
backgroundColor: '#000',
elevation: 0,
showdowOpacity: 0
},
})
},
Home: {
screen: AppDrawerNavigator,
navigationOptions: () => ({
header: null
})
},
});
With a DrawerNavigator nested inside:
const AppDrawerNavigator = createDrawerNavigator({
Home: {
screen: Home,
navigationOptions: {
drawerLabel: 'Home',
gesturesEnabled: false,
}
},
Favorites: {
screen: Favorites,
navigationOptions: {
drawerLabel: 'Favorites',
}
}
},
{
drawerPosition: 'left',
contentComponent: props => <Drawer {...props} />
});
The initial route of the stack navigator is working fine
Login -> Home
But when I try navigating from Home to Favorites it navigates immediately back to Home after rendering the Favorites screen.
I am using react-navigation#2.11.2 and react-native#0.56.0
With Home being used in both stack and drawer navigator.
There are high chances of name conflicts occurring here.
Try this structure.
const Stack = {
FirstView: {
screen: FirstView
},
SecondView: {
screen: SecondView
},
ThirdView: {
screen: ThirdView
}
};
const DrawerRoutes = {
FirstViewStack: {
name: 'FirstViewStack',
screen: StackNavigator(Stack, { initialRouteName: 'FirstView' })
},
SecondViewStack: {
name: 'SecondViewStack',
screen: StackNavigator(Stack, { initialRouteName: 'SecondView' })
},
ThirdViewStack: {
name: 'ThirdViewStack',
screen: StackNavigator(Stack, { initialRouteName: 'ThirdView' })
},
};
const RootNavigator =
StackNavigator({
Drawer: {
name: 'Drawer',
screen: DrawerNavigator(
DrawerRoutes,
),
},
...Stack
},
{
headerMode: 'none'
}
);
I faced a similar issue when i tried to use a hamburger menu in my Home page (which uses stack navigator to goto other pages).
Check this Git Article also.

How to disable Drawer in nested component in React Navigation

Consider the render of the Main component:
render() {
const { isAuthenticated } = this.props;
return (
<View>
{isAuthenticated ? <Dashboard /> : <Login />}
</View>
);
I want to lock the drawer in the Login component. Now i know that i could achieve this if Login wasn't a child of Main this way (in my Router component):
Login: {
screen: Login,
navigationOptions: () => ({
drawerLockMode: 'locked-closed',
}),
},
But since Login is a child of Main and Main has the drawer, Login will automatically have the drawer too. I've tried "overriding" it by calling this in Login:
static navigationOptions = {
drawerLockMode: 'locked-closed',
};
But no success. Here's my Router:
const Stack = {
Main: { screen: Main },
Login: {
screen: Login,
navigationOptions: () => ({
drawerLockMode: 'locked-closed',
}),
},
Outbox: { screen: Outbox },
Dashboard: { screen: Dashboard },
JobList: { screen: JobList },
CreateJob: { screen: CreateJob },
Reporting: { screen: Reporting },
JobDescription: { screen: JobDescription },
};
const DrawerRoutes = {
DrawerStack: {
name: 'DrawerStack',
screen: StackNavigator(
Stack,
{
initialRouteName: C.MAIN,
headerMode: 'none',
navigationOptions: {
gesturesEnabled: false,
},
}),
},
};
export const DrawerNavigation = StackNavigator({
Drawer: {
name: 'Drawer',
screen: DrawerNavigator(DrawerRoutes, {
contentComponent: DrawerPanel,
}),
},
...Stack,
}, { headerMode: 'none' });
Is there a way to achieve this ?