Reset react-navigation Stack from Drawer - react-native

A lot of issues opened on official react-navigation github: https://github.com/react-community/react-navigation/issues but no one gives a real solution, including one post saying "look at this problem, a lot of issues": https://github.com/react-community/react-navigation/issues/691 without answer.
Basically: I have a DrawerNavigator with some nested StackNavigator.
export const MainNavigator = DrawerNavigator(
{
OrderNew: {
screen: OrderNew,
navigationOptions: {
drawerLabel: DrawerLabel('New Order')
}
},
Orders: {
screen: OrderNavigator, //a StackNavigator
navigationOptions: {
drawerLabel: DrawerLabel('Orders')
}
},
MenuNavigator: {
screen: MenuNavigator, //a StackNavigator
navigationOptions: {
drawerLabel: DrawerLabel('Menu')
}
},
//more screens...
{
initialRouteName: 'Orders',
drawerPosition: 'right',
contentComponent: props => <ScrollView style={{backgroundColor: 'rgba(227, 100, 29, .95)'}}><DrawerMenu state={props} /></ScrollView>,
}
);
So, I'm passing a contentComponent with my own menu component. My idea is to overwrite the onItemPress event of DrawerItems component and put my logic here.
What I want is: I would like to reset MenuNavigator (and other stack screens) every time users press that item. I just can't achieve this. As i said, I tried a lot of code. Actually, it's like this:
onItemPress={
router => {
const navigateAction = NavigationActions.navigate({
routeName: router.route.routeName,
});
this.props.state.navigation.dispatch(navigateAction);
}
}
The above code works, I can navigate through drawer items. Now, in my head, I just should pass an subaction resetting to the first item on navigator. So I tried:
onItemPress={
router => {
const navigateAction = NavigationActions.navigate({
routeName: router.route.routeName,
action: NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({routeName: router.route.routes[0].routeName}),
]
})
});
this.props.state.navigation.dispatch(navigateAction);
}
}
And explodes an error:
There is no route defined for key Menu.
Must be one of: 'Orders','OrderClient'
Summarizing: how could I reset StackNavigator when I do navigate to another DrawerNavigator's item?

You are missing key: null.
Navigation actions should be like this.
action: NavigationActions.reset({
index: 0,
key: null,
actions: [
NavigationActions.navigate({routeName: router.route.routes[0].routeName}),
]
})

Related

No route found for key in stack navigator in react native

I am making a react native project which got an error for no route found in stack navigator.First of all here is my code for navigation of my app.
const RootStack = createStackNavigator({
Splash : { screen: Splash },
ModelProfile: { screen: ModelProfile},
HomeModel: {screen: modeldrawerNavigator},
EnterOtp : { screen: EnterOtp},
OtpChangePassword : { screen: OtpChangePassword},
}, {
headerMode: 'none',
initialRouteName: 'Splash'
})
const AppNavigator = createAppContainer(RootStack)
export default AppNavigator;
Now the code for modelDrawerNavigator is
const modeldrawerNavigator = createDrawerNavigator(
{
HomeScreen: { screen: homeStack},
EditProfile : { screen: profileStack},
BuyStar : { screen: StarStack},
Notifications : { screen: nStackmodel},
ResetPassword : { screen: ResetPassword},
},
{
initialRouteName: 'HomeScreen',
gesturesEnabled: true,
contentComponent: props => <DrawerModel {...props} />
},
);
And code for StarStack is
const StarStack = createStackNavigator({
Buy : { screen: BuyStars},
PaymentMode : { screen: PaymentMode},
}, {
headerMode: 'none',
initialRouteName: 'Buy'
});
Now when navigating inside drawer i want to clear stack to position 0.
<TouchableOpacity
onPress={() => {
this.props.navigation.navigate('BuyStar');
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Buy' })],
});
this.props.navigation.dispatch(resetAction);
}}
>
<Text style={styles.drawertext}>Buy Stars</Text>
</TouchableOpacity>
Above code is what produces an error that
No route defined for key 'Buy',Must be one of 'Splash','EnterOtp' etc.
I am stuck in this situation . Please help me to resolve this issue.
Thanks in advance .
It seems like the issue is your code is attempting to navigate the root navigator (Splash and EnterOp are both keys on this navigator). According to the docs:
key - string or null - optional - If set, the navigator with the given key will reset. If null, the root navigator will reset.
So maybe you can try adding the key param to your reset action:
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Buy' })],
key: 'BuyStar',
});
Here are some more resources that may help:
SO answer
Github Issue
Note that the implementation will likely depend on if you're using React Navigation 3.0+ or one of the earlier versions.
EDIT
The supplied key should be BuyStar - you're trying to navigate to the route Buy, which exists in the StarStack navigator. The StarStack navigator is located in the drawer navigator, so the key of this navigator is found in its keys:
const modeldrawerNavigator = createDrawerNavigator({
...
BuyStar : { screen: StarStack },
...
The key should be the id of the navigator that contains the route you're trying to navigate to.

Back button handler working on every screen after changing drawer navigator in react - native

I am making a react native application in which there can be two type of user model and member.Now each having different drawer navigator as both having different data and both these drawer are inside a main stack navigator which is main navigator for the application. Now in both the both the drawer navigator .
const RootStack = createStackNavigator({
Splash : { screen: Splash },
PrivacyPolicy : { screen: PrivacyPolicy},
Welcome : { screen: Welcome },
Login : { screen: Login },
HomeMember: {screen: memberdrawerNavigator},
HomeModel: {screen: modeldrawerNavigator},
OtpChangePassword : { screen: OtpChangePassword},
}, {
headerMode: 'none',
initialRouteName: 'Splash'
})
I have a homepage different for both the member type and in both the home page i have implemented the back handler like this.
handleBackWithAlert = () => {
if (this.props.isFocused) {
if(this.state.loading_status){
this.setState({loading_status:false})
}
else{
Alert.alert(
'Exit App',
'Exiting the application?',
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel'
},
{
text: 'OK',
onPress: () => BackHandler.exitApp()
}
],
{
cancelable: false
}
);
}
return true;
}
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress',this.handleBackWithAlert);
//this.props.navigation.dispatch(DrawerActions.closeDrawer());
this.props.navigation.closeDrawer();
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackWithAlert);
}
Since i wanted to check back handling on only specific page so i exported my component like this.
export default withNavigationFocus(HomePage);
Now what the problem is that suppose i am using member account and after that i logout and then login with model account then this back handler is working on every screen why ? And if i close or kill that app without logout and restart then this problem disappears.
Here is my code called on logout
AsyncStorage.clear()
ToastAndroid.show("Logged Out Successfully !",ToastAndroid.SHORT);
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Welcome' })],
});
this.props.navigation.dispatch(resetAction);
Kindly help me to solve this problem. Thanks in advance.
According to my understanding, something is wrong with your BackHandler listeners. maybe you're not removing BackHandler listener on componentWillUnmount?

React Navigation - Jumping between tabs

Hello guys I have 5 Containers in my tab bar and I would like to know if there is an option to return to the main screen if I select another tab container.
So I have the following
https://snack.expo.io/#react-navigation/stacks-in-tabs-v3
While I am in Home tab I click on Go to Details and then I switch to Settings tab. If I return to Home container I want to see the default screen which is HomeScreen
Any option?
You can redefine the tabBarOnPress of the navigationOptions of each StackNavigator like so:
const stacks = [
{ Home: HomeScreen, Details: DetailsScreen },
{ Settings: SettingsScreen, Details: DetailsScreen }
].reduce((stacksObj, stack) => {
const initialRoute = Object.keys(stack)[0];
stacksObj[initialRoute] = createStackNavigator(
stack,
{
navigationOptions: {
tabBarOnPress: ({ navigation }) => navigation.navigate({
routeName: initialRoute,
action: navigation.popToTop()
})
}
}
);
return stacksObj
}, {});
export default createAppContainer(createBottomTabNavigator(
stacks,
{ /* same defaultNavigationOptions as the snack example */ }
));
Cause your DetailScreen is nested in stack Home, you must navigate one more level to that screen
_navigateHome = () => {
const navigateAction = NavigationActions.navigate({
routeName: 'Home',
action: NavigationActions.navigate({ routeName: 'Home' }),
});
this.props.navigation.dispatch(navigateAction);
}
Looks the api has changed , the new way to achieve this is :
import { CommonActions } from '#react-navigation/native';
navigation.dispatch(
CommonActions.navigate({
name: 'Profile',
params: {
user: 'jane',
},
})
);

RESET navigation - Drawer route with animation

Before all, I have to precise I searched arround as for example https://github.com/react-community/react-navigation/issues/1000 , https://github.com/react-community/react-navigation/issues/307 or even StackOverflow.
I have the following code :
/**** STACKNAVIGATOR 2 ****/
const HomeDrawer = StackNavigator({
HomeDrawerstack: { screen: Home, navigationOptions: { title: 'Accueil', } },
}, { initialRouteName: 'HomeDrawerstack', }
);
/**** STACKNAVIGATOR 3 ****/
const AddIdeaDrawer = StackNavigator({
AddIdeaDrawerRoot: { screen: AddIdea, navigationOptions: { title: 'AddIDea', } },
}, { initialRouteName: 'AddIdeaDrawerRoot', }
);
/**** DRAWERNAVIGATOR ****/
const DrawerRoutes = {
HomeRoot: {
screen: HomeDrawer,
navigationOptions: {
title: 'Home',
},
},
AddIdea: {
screen: AddIdeaDrawer
navigationOptions: {
title: 'Add an idea',
},
},
};
const DrawerOptions = {
initialRouteName: 'HomeRoot',
contentComponent: CustomDrawerContentComponent,
drawerWidth: 300,
};
export const Drawer = DrawerNavigator(DrawerRoutes, DrawerOptions);
/**** STACKNAVIGATOR 1 ****/
export const Navigation = StackNavigator({
HomeDrawerRoot: { screen: Drawer, navigationOptions: { header: null } },
OtherRoot: { screen: Contacts, navigationOptions: { /** **/ } },
}, { initialRouteName: 'HomeDrawerRoot', }
);
What I have tried
Currently, I'm trying to navigate from HomeDrawerstack to AddIdeaDrawerRoot with the StackNavigator animation and preventing the user to go back.
I have tried different solutions I have found on the different issues but any of them solve this problem.
First try :
navigate('AddIdea',)}
The app goes to AddIdea perfectly. However, the user can go back (that's normal I don't use RESET) and there is no animation. The AddIdea screen appears like that without any transition.
Second try :
NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({routeName: 'AddIdea',})
]
})
It doesn't work.
The logs prints : There is no route defined for key AddIdea. Must be one of: 'HomeDrawerstack'
Third try :
I read on an another issue https://github.com/react-community/react-navigation/issues/#1127 which explains that the key:null was the solution. So I tried the following action :
NavigationActions.reset({
index: 0,
key: null,
actions: [
NavigationActions.navigate({
routeName: 'HomeDrawerRoot',
action: NavigationActions.navigate({ routeName: 'AddIdea',}),
})
]
})
The app goes well to AddIdea. However, still with the possibility of going back to the last page (Home) and without animation.
Fourth try :
navigate({
routeName: 'HomeDrawerRoot',
action: NavigationActions.navigate({
routeName: 'AddIdea', action: NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: AddIdeaDrawerRoot
]
})
})
})
It doesn't work. Printing : There is no route defined for key AddIdeaDrawerRoot. Must be one of: 'HomeDrawerstack'
What I'm trying to do
The only thing I want is going from HomeDrawerstack (Home screen) to AddIdeaDrawerRoot (AddIdea screen). With an animation like the StackNavigator animation and preventing the user of going back. (Disable Back action).
After reading a lot of issues, I didn't find any solutions. Is it a forget of the react-navigation ? Or I'm the problem ?! Thanks in advance.
My current configuration
| software | version
| ---------------- | -------
| react-navigation | ^1.0.0-beta.11
| react-native | 0.45.1
| node | 7.4.0
| npm or yarn | 0.24.5
I had similar problems and could not find a good example app. My navigation in my app crashed the app which was very frustrating. In the end I found a good working project to base my code on: https://github.com/paraswatts/DrawerNavigatorReactNative/
This project has working navigation with both tabs and drawers with no crashes and clean code. I hope it can help you.

React Navigation: Applying separate transitions to scenes

I want to apply different transitions to scenes in my StackNavigator in React Navigation, for my React Native (iOS) app.
export const AppNavigator = StackNavigator({
Home: { screen: Home },
NewChild: { screen: NewChild },
Journal: { screen: Journal }
});
I might want NewChild to load from the bottom up, like a modal. Or I might want it to slide in from right to left. It would great if this could be customised. From the documentation, it seems you can only set your StackNavigation to either card or modal for the whole thing.
Thanks to everyone in the thread, it worked for me.
I have many scenes managed by only one StackNavigator and I wanted to make a different animation for some of them.
First of all, I made myself a constant in my router which included every scenes I wanted to display with horizontal transition :
const horizontalTransitionScenes = [
AddContact,
];
After that, I designed my app router with StackNavigator :
const App = StackNavigator({
AddContact: {
screen: AddContact,
},
EditContact: {
screen: EditContact,
},
});
I used CardStackStyleInterpolator to modify the transitions between scenes. I can be imported like this : import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator';
We can now add transitionConfig params to our StackNavigator to display a different animation depending on our scenes :
const App = StackNavigator(
{
AddContact: {
screen: AddContact
},
EditContact: {
screen: EditContact
}
},
{
transitionConfig: (currentState: any) => {
if (
currentState.scenes[
currentState.scenes.length - 1
].route.routeName.includes(horizontalTransitionScenes)
) {
return {
screenInterpolator: CardStackStyleInterpolator.forHorizontal
};
}
return {
screenInterpolator: CardStackStyleInterpolator.forVertical
};
}
}
);
With that StackNavigator implementation, you will be able to display different kind of animations for the scenes you want.
Have a good day 🎉
As you already saw in the docs, it's not possible right now. You can track this issue and this issue requesting this feature. It's on the roadmap for v2, but since v1 isn't even released yet, your best bet is probably to either:
Implement your own transitionConfig that somehow handles this per screen. I'm not sure if this is possible.
Fork an implement something using the ideas from the main issue. Or simple make your fork specific to your code and maintain it until that issue is resolved.
I was able to accomplish this by doing some conditional logic, we're on react-navigation 3.3.2.
You have to import Easing and Animated from react-native and then you can conditionally render transitions for a specific screen by name, or also by index (below is by name).
By declaring the config outside the navigator, and toggling transitionSpec with an {} it will only trigger on that particular screen -- same with the conditional for screenInterpolator.
Screen1 to Screen2 has the animation, but none of the other screens have that animation.
const screen2Config = {
duration: 300,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
};
export const ScreenStack = createStackNavigator({
Screen1: {
screen: Screen1,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen1',
headerTitleAllowFontScaling: false,
}),
},
Screen2: {
screen: Screen2,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen2',
headerTitleAllowFontScaling: false,
tabBarVisible: false,
}),
},
Screen3: {
screen: Screen3,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen3',
headerTitleAllowFontScaling: false,
}),
},
Screen4: {
screen: Screen4,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen4',
headerTitleAllowFontScaling: false,
}),
},
}, {
headerMode: 'float',
mode: 'modal',
transitionConfig: sceneProps => ({
transitionSpec: sceneProps.scene.route.routeName === 'Screen2' ? screen2Config : {},
screenInterpolator: (sceneProps) => {
if (sceneProps.scene.route.routeName === 'Screen2') {
const { layout, position, scene } = sceneProps;
const { index } = scene;
const width = layout.initWidth;
const translateX = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [width, 0, 0],
});
const opacity = position.interpolate({
inputRange: [index - 1, index - 0.99, index],
outputRange: [0, 1, 1],
});
return { opacity, transform: [{ translateX }] };
}
},
}),
});