react-navigation deep linking with multiple paths - reactjs

Is there any way to configure react-navigation so that single screen can handle multiple links?
Each screen in StackNavigator can have an optional property path which enabled deep links, StackNavigator also accepts paths option that lets u override paths per specific screen but it's still one-to-one mapping.
Is there a way to declare unlimited amount of paths that should be handled by single screen?

You can use variables for unlimited number of paths like its shown in StackNavigator docs
Example from docs
StackNavigator({
// For each screen that you can navigate to, create a new entry like this:
Profile: {
// `ProfileScreen` is a React component that will be the main content of the screen.
screen: ProfileScreen,
// When `ProfileScreen` is loaded by the StackNavigator, it will be given a `navigation` prop.
// Optional: When deep linking or using react-navigation in a web app, this path is used:
path: 'people/:name',
// The action and route params are extracted from the path.
// Optional: Override the `navigationOptions` for the screen
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.name}'s Profile'`,
}),
},
...MyOtherRoutes,
});
Update
You can create a custom route handler for more detailed control over paths shown here.
Example from docs
import { NavigationActions } from 'react-navigation'
const MyApp = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
}, {
initialRouteName: 'Home',
})
const previousGetActionForPathAndParams = MyApp.router.getActionForPathAndParams;
Object.assign(MyApp.router, {
getActionForPathAndParams(path, params) {
if (
path === 'my/custom/path' &&
params.magic === 'yes'
) {
// returns a profile navigate action for /my/custom/path?magic=yes
return NavigationActions.navigate({
routeName: 'Profile',
action: NavigationActions.navigate({
// This child action will get passed to the child router
// ProfileScreen.router.getStateForAction to get the child
// navigation state.
routeName: 'Friends',
}),
});
}
return previousGetActionForPathAndParams(path, params);
},
});

Related

React-navigation crash when going back from a nested navigation

I'm using React-navigation 6 on my React native application
I have two StackNavigator in a BottomStackNavigator with those screens and params on each stack :
export type BottomNavigationStack = {
Planner: undefined,
Profile: undefined,
}
export type PlannerNavigationStack = {
PlannerHome: undefined,
PlannerDetails: {
detail: { ... }
},
Profile: {
screen: string,
initial?: boolean,
params?: { [name: string]: string }
}, // We can go to ProfileNavigationStack from PlannerNavigationStack
}
export type PlannerNavDetailProps = StackScreenProps<PlannerNavigationStack, 'PlannerDetails'>
export type ProfileNavigationStack = {
ProfileHome: undefined,
ProfileAddThings: {
service: string
from: string,
},
Planner: {
screen: string,
initial?: boolean,
params?: { [name: string]: string }
}, // We can go to PlannerNavigationStack from ProfileNavigationStack
}
export type ProfileNavAddThingsProps = StackScreenProps<ProfileNavigationStack, 'ProfileAddThings'>
So i'm trying to navigate from PlannerStack to ProfileStack, when I'm in ProfileStack, I want to go back to PlannerStack.
I got no problem going to ProfileStack from PlannerStack like this :
navigation.navigate('Profile', {
screen: 'ProfileAddThings',
params: {
service: 'myservice',
from: 'myfrom',
},
initial: false,
})
So at that moment I'm on ProfileStack on screen ProfileAddThings.
if I read the documentation, the PlannerStack keep it's history, and if I click on the bottomTab button of Planner I can see the page is still PlannerDetails.
But, when I click on the back button of the screen ProfileAddThings, I'm going back to ProfileHome.
I tried to overide the backButton action of the screen ProfileAddThings with that code :
navigation.navigate('Planner', { screen: 'PlannerDetails' })
But I got the error : undefined is not an object (evaluating 'route.params.detail')
Detail is a parameter of PlannerDetails screen.
I really don't understand why this parameter is undefined because de PlannerStack history is still present.
Someone has already gone back from a nested navigation in react native ?
Thanks
You need to get the parent navigator.
You can do this by using the getParent() method from the current screen.
Will be something like this:
let parent = navigation.getParent(); // This will assing PlannerStack to the parent variable.
then you should call navigation method with the Route name
parent.navigate('PlannerDetails');
The route history, register the hole component from the screen (in your case PlannerStack) and in your PlannerStack the innitialRoute probably is ProfileHome, thats the reason you get there when using the back button.

Reseting React nested Navigators within render

Context: building a React Native mobile application. Along the bottom of the UI are four tabs. Three contain simple StackNavigators to preserve navigation state within that tab. The fourth screen is itself a tab navigator. The screen of each sub-tab is a StackNavigator to preserve their own navigation history while within the tab.
Outline:
The top level navigator:
BottomTabNavigator
|_Tab 1 (StackNavigator) (initial route)
|_Tab 2 (StackNavigator)
|_Tab 3 (StackNavigator)
|_Tab 4 (Tab4Container)
The sub-navigator:
Tab4Navigator (MaterialTopTabNavigator)
|_Subtab 1 (StackNavigator) (initial route)
|_Screen 4.1.1 (initial route)
|_Screen 4.1.2
|_Subtab 2 (StackNavigator) (initial route)
|_Screen 4.2.1 (initial route)
|_Screen 4.2.2
Tab 4 has a few components above the tabs themselves, so that's wrapped in a container. Essentially:
class Tab4Container extends React.router {
render() {
return(
<View>
<Header />
<Tab4Navigator navigation={this.props.navigation}>
</View>
)
}
}
Issue: The nested tab navigator doesn't reset its state when the user leaves Tab4 and comes back. First, the user clicks to Tab4, then Subtab 2, then Screen 4.2.2. Then the user clicks on Tab 2. Then the user returns to Tab 4. The current screen should be 4.1.1 but it is still 4.2.2.
Tab4Navigator is structured:
const Tab4Navigator = createMaterialTopTabNavigator(
{
subTab1: createStackNavigator(
{
screen_4_1_1: {
screen: Screen411Container
},
screen_4_1_2: {
screen: Screen412Container
},
},
{ initialRouteName: "screen_4_2_1" }
),
subTab2: createStackNavigator(
{
screen_4_2_1: {
screen: Screen421Container
},
screen_4_2_2: {
screen: Screen422Container
},
},
{ initialRouteName: "screen_4_2_1" }
),
},
{ initialRouteName: "subTab1" }
)
Question: How can I reset the StackNavigator when the Tab4Navigator is re-rendered?
You have to add tabBarOnPress event on Tab4Navigator and then reset stack . Check this GitHub link also
import { StackActions, NavigationActions } from 'react-navigation';
......
{ initialRouteName: "subTab1", tabBarOnPress: this.handleTabPress }
handleTabPress = ({ navigation }) => {
navigation.popToTop();
navigation.navigate(navigation.state.routeName);
}

How to reset my stack in react-navigation?

I have 5 tabs on my Home Screen.
Home Tab
Search tab
AddPostTab
Notifications Tab
Profile Tab
AddPostTab is like this.
const AddPostTab = createStackNavigator({
AddPost: {
screen: AddPost,
},
ImageDescription: {
screen: ImageDescription
},
},
{
headerMode:'none',
mode:'modal'
}
);
When I go back from ImageDescription screen to Home Screen and then if I go to AddPostTab again, I'm directly going to the ImageDescription screen. But I want to be able to go to AddPost screen.
I've also tried
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'AddPost' }),
],
});
this.props.navigation.dispatch(resetAction);
but this only takes me to the AddPost Screen. But if I use Home instead of AddPost, it doesn't works.
How can I reset my stack in my case so that I can go to Home screen?
These are completely different navigators. And reset can't be applied to TabNavigator, because it's not much sense to do it.
What you can do - is do something like this:
ImageDescription.js:
goToHome() =>
this.props.navigation.popToTop()
&& this.props.navigation.navigate('Home');
This will reset to root your current stack and then you will switch to Home tab
if you are have a single stack then use this on button click
navigation.dispatch(
CommonActions.reset({
index: 1,//the stack index
routes: [
{ name: 'Promote' },//to go to initial stack screen
],
})
)
for single stack you can also use the
navigation.popToTop()
but if you are wroking on tabs and then want to reset the tab then try this
<BottomTab.Screen
name="Post"
component={PostStack}
options={{
unmountOnBlur: true,// set this props in your tab screen options
title: 'Post',
tabBarIcon: focused => <TabBarIcon focused={focused} name="Post" />,
}}
/>
You can run this from the screen you want to reset the stack with any event.
This fixed the similar issue for me (React Navigation 5.x)
import {NavigationActions} from 'react-navigation';
navigation.reset(
[NavigationActions.navigate({routeName: 'Profile'})],
0,
);

Navigate to parent of parent stack

I have this scenario where i have a StackNavigator nested in a TabNavigator nested in another StackNavigator.
const TabOneStack = StackNavigator({
ScreenA: { screen: ScreenA },
ScreenB: { screen: ScreenB }
});
const MainTabs = TabNavigator({
TabOne: { screen: TabOneStack },
TabTwo: { screen: TabTwoStack }
});
const Root = StackNavigator({
HomeScreen: { screen: HomeScreen },
MainTabs: { screen: MainTabs }
});
Everything works but i cant figure out how to navigate for example from ScreenA back to the Home screen in the root StackNavigator.
After the HomeScreen the User navigates directly to ScreenA.
The back button in the header in ScreenA works fine and brings me back to Home but need a way to have a button that brings me back to the HomeScreen.
this.props.navigation.goBack() does not work unfortunately.
i also tried
const backAction = NavigationActions.reset({
index: 0,
key: null,
actions: [
NavigationActions.navigate({ routeName: 'HomeScreen'})
]
});
this.props.navigation.dispatch(backAction));
but i get:
There is no route defined for key HomeScreen. Must be one of: 'ScreenA', 'ScreenB'.
What is the correct way to do this?
To traverse from child StackNavigator back to parent StackNavigator, do:
class ScreenA extends Component {
render() {
return (<Button onPress={() => {
this.props.navigation.dispatch({type: 'Navigation/BACK'});
}} title='ScreenA. back to Home' />);
}
}
this.props.navigation.dispatch() with 'Navigation/BACK' works exactly the same as the top-most back button.
It differs from goBack() by apply parent-child stack traversing, while goBack() does not.
Result:
And note #Jigar's answer is also correct, it's just a shorthand notation of mine. The key is to pass null argument into goBack(). It won't work without that.
this.props.navigation.goBack(null);
In the new react native version we use 'StackActions' for this case:
StackActions reference
the 'key' parameter in this case is the key to solve this problem. Usually we manage the stack like this:
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'ScreenA' })],
});
this.props.navigation.dispatch(resetAction);
but StackActions object has another option and its: 'key' - its an optional string or null - If set, the navigator with the given key will reset. If null, the root navigator will reset.
So in this case, to reset back to root and navigate to some screen inside it -> in your case from 'ScreenA' to 'HomeScreen' - you should write:
this.props.navigation.dispatch(
StackActions.reset({
index: 0,
key:null,
actions: [
NavigationActions.navigate({
routeName: 'HomeScreen'
})
]
})
);
Notice, in this new version we no longer use 'NavigationActions' to reset the stack, only 'StackActions'.
use this
this.props.navigation.goBack(null);
In v6 you can use something like this:
navigation.getParent()?.navigate('Home')
For me, it's should working fine by using this:
Change this:
this.props.navigation.dispatch(backAction));
to
this.navigator.dispatch(backAction);
Also in your render
render() {
return (
<Root
ref={(nav) => {
this.navigator = nav;
}}
/>
);
}

React Navigation redux navigate

I'd like to know how the navigation supposed to happen, when redux stores the navigation state.
Short version:
If the redux store isn't in initial state, a screen is mounted without actually navigationg there.
Detailed description:
Now I'm able to navigate either by (1) using the navigation props given by the parent navigator (StackNavigator in my case) or by (2) dispatching an action.
1: this.props.navigation.navigate('main')
2: this.props.navigateToMainAction()
The reducer:
const INIT_STATE = Nav.router.getStateForAction(
NavigationActions.navigate({ routeName: 'login' })
);
const navReducer = (state = INIT_STATE, action) => {
const newState = Nav.router.getStateForAction(action, state);
return newState || state;
};
Nav structure:
const authStack = StackNavigator({
login: { screen: LoginScreen }
,forgottendPassword: { screen: LoginScreen }
}, {
initialRouteName: 'login'
,headerMode: 'none'
});
const homeDrawer = DrawerNavigator({
home: {
screen: HomeScreen
,navigationOptions: { drawerLockMode: 'locked-closed' }
}
}, {
drawerPosition: 'right'
,drawerWidth: 300
,contentComponent: props => <HomeDrawerMenu {...props} />
});
const mainStack = StackNavigator({
homeDrawer: {
screen: homeDrawer
,navigationOptions: ({ navigation }) => ({
header: <HomeMenu navigate={navigation.navigate} />
})
}
,partnerList: {
screen: PartnerListScreen
,navigationOptions: ({ navigation }) => ({
header: <PartnerListMenu navigation={navigation} />
})
}
}, {
initialRouteName: 'homeDrawer'
});
const Nav = StackNavigator({
auth: { screen: authStack }
,main: { screen: mainStack }
}, {
initialRouteName: 'auth',
headerMode: 'none',
});
When I try to navigate from auth/login to main and from there to main/partnerList it only works correctly, if the redux store is in initial state (after I cleared the async storage).
But when I reload the app, it mounts the main/partnerList component, without actually navigating there and thanks to that, the action which fetching the partnerList comp.'s data is also called.
Expected action order on load:
##INIT
Offline/STATUS_CHANGED
persist/REHYDRATE
check_token <- Check if the user is logged in (in this case he is)
check_token_commit
Navigation/NAVIGATE <- The user is forwarded to main, when he is logged in
Navigation/NAVIGATE <- The user goes to the partnerList component
fetch_partner_list <- Action for fetching the partner list
Offline/BUSY
fetch_partner_list_commit
Current action order on load:
##INIT
Offline/STATUS_CHANGED
persist/REHYDRATE
check_token <- Check if the user is logged in (in this case he is)
fetch_partner_list <- It's already fetching, but no navigate action was triggered yet
Offline/BUSY
check_token_commit
Navigation/NAVIGATE <- The user is forwarded to main, when he is logged in
Navigation/NAVIGATE <- The forwarding happens twice, for some reason
fetch_partner_list_commit
Navigation/NAVIGATE <- The user goes to the partnerList component
fetch_partner_list <- The only time it should be triggered
Offline/BUSY
fetch_partner_list_commit
The problem was coused by the inappropriate use of the react navigation.
I had to wrap the auth and main router and pass down in screenProps the root navigation object.

Resources