react-navigation 3 reset in nested stack - react-native

Im try to understand how to reset in nested stack
this my code
const AuthStack = createStackNavigator(
{
Welcome,
Login,
Register,
ConfirmationCode,
},
{
initialRouteName: 'Welcome',
headerMode: 'none',
lazy: true,
transitionConfig,
defaultNavigationOptions: {
gesturesEnabled: false,
},
}
)
const AppStack = createStackNavigator(
{
TabStack,
SearchResult,
BusinessDetail,
BusinessMap,
MakeAppointment,
TermsAndConditions
},
{
initialRouteName: 'TabStack',
headerMode: 'none',
lazy: true,
transitionConfig,
defaultNavigationOptions: {
gesturesEnabled: false,
},
}
)
let MainStack = createSwitchNavigator(
{
AuthLoading,
Auth: AuthStack,
App: AppStack,
},
{
initialRouteName: 'AuthLoading',
headerMode: 'none',
lazy: true,
defaultNavigationOptions: {
gesturesEnabled: false,
},
}
)
TabStack
import React from 'react';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import {
Search,
MyFavourites,
MyAppointments,
UserProfile
} from '../screens'
import Icon from 'react-native-vector-icons/Feather';
import Colors from '../utils/Colors'
let TabStack = createBottomTabNavigator(
{
Search,
MyFavourites,
MyAppointments,
UserProfile,
},
initialRouteName: 'ScreenTab1',
tabBarOptions: {
activeTintColor: Colors.pink,
inactiveTintColor: Colors.black,
showLabel: false,
style: {
backgroundColor: 'white'
}
},
}
)
export default createAppContainer(TabStack);
I want to understand how to make reset for example:
reset from UserProfile to TabStack (in AppStack) to AuthStack
I tried to do from it this way
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'AuthStack' })],
});
this.props.navigation.dispatch(resetAction);
or this way
const resetAction = StackActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: 'AuthStack' })],
});
this.props.navigation.dispatch(resetAction);
but i got the error
there is no route defined for AuthStack
I checked in issues in stackoverflow but the answers there not works for me,always show me the same error I wrote above.

Your resetAction is unsuccessful because you are dispatching it on TabStack (because you are calling this.props.navigation.dispatch on UserProfile, if I get you correctly). You need to dispatch the resetAction to your MainStack instead. This thread here suggested some ways that you can achieve this. And also, here is my preferred solution, because i don't have to pass props around navigators or calls multiple nested actions with this.
Create a navigationService.js with the following contents (to keep your top level navigator as a reference)
import { NavigationActions, StackActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigateMainNavigator(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
}),
);
}
// add other navigation functions that you need and export them
export default {
setTopLevelNavigator,
navigateMainNavigator,
};
On your App.js or any other file you render your MainStack, do this to set the reference
import NavigationService from './navigationService';
...
render() {
return (
...
<MainStack
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
...
)
}
And wherever when you want to reset to your AuthStack (or any other stack in your MainStack), just import NavigationService and call
NavigationService.navigateAndReset('Auth', {...yourParamsIfAny});
// 'Auth' because you named it that way in your 'MainStack'
===========================================================================
Edited
Previous solution, in navigationService.js, is for StackNavigator as the MainStack
function navigateAndReset(routeName, params) {
_navigator.dispatch(
StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName,
params,
}),
],
})
);
}

Try by setting it to AppStack, because anyhow it is going to redirect to GeneralStack as you have it as initialRouteName inside AppStack
const resetAction = StackActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: 'App' })],
});
this.props.navigation.dispatch(resetAction);

You can do the following, to reset to authStack,
create a reset actions as following,
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: "AuthStack" })],
key: null
});
this.props.navigation.dispatch(resetAction);
and also add the AuthStack into appStack, or the stack from which you are calling the above code.
For example if your calling this from your appstack, add the following line as a route within your app stack
const AppStack = createStackNavigator(
{
TabStack,
SearchResult,
BusinessDetail,
BusinessMap,
MakeAppointment,
TermsAndConditions,
AuthStack <---Insert THIS
},
This works for me when using to signout.

Related

Navigate to different stack - Can't perform update on unmounted component (no-op)

I'm using react-native-navigation v3 and I'm trying to go from a screen in one stack to another screen in a different stack.
When I trigger onDetailButtonPress from the LoggedInStack, I'm able to open the ReleaseDetail screen but the bottomTabNavigator disappears and I get an error: Can't perform a React state update on an unmounted component.
What is causing this issue here? I'm able to navigate from the HomeStack to the LoggedInStack but not the other way around.
HomeStack.js:
const HomeStack = createStackNavigator(
{
Home: {
screen: HomeScreen
},
Settings: {
screen: SettingsScreen
},
ReleaseDetail: {
screen: ReleaseInfoContainerScreen
},
SettingOnboard: {
screen: OnboardStack
}
},
{
headerMode: 'none'
}
)
LoggedInStack.js:
onDetailButtonPress={() => {
this.props.navigation.navigate(
NavigationActions.navigate({
routeName: 'HomeStackView',
params: {},
action: NavigationActions.navigate({
routeName: 'ReleaseDetail',
params: {
releaseId: selectedReleaseId
}
})
})
)
}}
export const LoggedInStack = createStackNavigator(
{
TabNavigator: {
screen: connectedLoggedInTabContainerView,
navigationOptions: {
header: null
}
},
FullScreenPlayer: {
screen: PlayerScreen,
navigationOptions: {
header: null
}
},
},
{
mode: 'modal',
headerMode: 'none'
}
)
Try to use:
onDetailButtonPress={() =>
this.props.navigation.navigate('ReleaseDetail', { releaseId: selectedReleaseId })
}

Increased memory usage for both IOS and Android in react-navigation nested routes

I have a deeply nested route as shown below.
const ReturnFlows = createStackNavigator(
{
OrderSelectScreen,
ItemTBScreen,
},
{
initialRouteName: 'OrderSelectScreen'
},
);
const CheckoutFlows = createStackNavigator(
{
ReviewScreen,
CheckoutScreen,
},
{
initialRouteName: 'ReviewScreen'
},
);
const ItemFlowScreens = createSwitchNavigator(
{
ItemTAScreen,
ReturnFlows,
CheckoutFlows,
CompletedScreen,
},
{
initialRouteName: 'CheckoutFlows',
},
);
const CreateOrderScreens = createStackNavigator(
{
CustomerScreen,
CSelectedScreen,
WarehouseScreen,
WSelectedScreen,
ItemFlowScreens,
},
{
headerMode: 'none',
},
);
const Main = createStackNavigator(
{
...otherRoutes,
CreateOrderScreens,
HomeScreen,
},
{
headerMode: 'none',
},
);
The Routes above roughly translate to the diagram below
Everything works fine but while I go deeper into the nested screens I noticed the memory usage spikes to 300mb. Additionally in the final CompletedScreenI have the following function to jump back to the initial home page.
const goHome = () => {
_navigator.dispatch(
StackActions.reset({
key: null,
index: 0,
actions: [NavigationActions.navigate({ routeName: 'HomeScreen' })],
}),
);
};
Even though i have enabled useScreens from react-native-screensand have jumped to the initial screen the memory usage does not seem go down. How can I overcome this issue ?

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',
},
})
);

StackNavigator in DrawerNavigator

I using React Navigation in my react native app, and I have a simple route, HomeScreen and QScreen should have a access to drawer, but SplashScreen and LoginScreen no!
How can I do this?
This code not working and raise an error in read params in HomeScreen (that called in SplashScreen) :
const MainStack = createStackNavigator({
HomeScreen: { screen: HomePage, navigationOptions: noHeadersNavigationOptions },
QScreen: { screen: QPage, navigationOptions: noHeadersNavigationOptions },
}, {
initialRouteName: 'HomeScreen',
});
const DrawerStack = createDrawerNavigator({
MainStack ,
}, {
initialRouteName: 'MainStack',
});
const Router = createStackNavigator({
SplashScreen: { screen: SplashScreenPage },
LoginScreen: { screen: LoginPage },
HomeScreen: DrawerStack,
}, {
initialRouteName: 'SplashScreen',
}
);
export default createAppContainer(Router);
In SplashScreenPage :
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'HomeScreen', params: params })],
});
this.props.navigation.dispatch(resetAction);
I achieved a similar result using this navigation structure :
MainSwitch
LoginPage
MainDrawer
Page1
Page2
Page3
This is a working example :
https://snack.expo.io/#sanjar/c28tNT

Remove screen from stack navigator

I’ve observed Back button logic works seeing the sequence of screens in the stack. I’ve a Drawer navigator placed inside stack navigator.
On top of the stack I’ve Splash screen. On Dashboard when I press back button it takes me to splash screen.
How can I remove splash screen from stack after entering in the app, So when I press back button Dashboard it will EXIT the app instead of taking to SPLASH SCREEN.
/* #flow */
import React from "react";
import { Platform, Text } from "react-native";
import { Root } from "native-base";
import { StackNavigator, DrawerNavigator} from "react-navigation";
import Register from "./components/Register";
import Available from "./components/Available";
import Splash from "./components/splash/“;
import SideBar from "./components/sidebar";
import Discover from "./components/Discover/";
import Dashboard from "./components/Dashboard/";
import Contact from "./components/Contact"
const Drawer = DrawerNavigator(
{
Dashboard: { screen: Dashboard },
Discover: { screen: Discover },
Contact: { screen: Contact},
},
{
navigationOptions: {
gesturesEnabled: false,
},
initialRouteName: "Dashboard",
contentOptions: {
activeTintColor: "#e91e63"
},
drawerPosition: 'right',
contentComponent: props => <SideBar {...props} />
}
);
const AppNavigator = StackNavigator(
{
Splash: { screen: Splash },
Drawer: { screen: Drawer },
Available: { screen: Available },
Register: { screen: Register },
},
{
// initialRouteName: “Splash”,
headerMode: "none",
}
);
export default () =>
<Root>
<AppNavigator />
</Root>;
One solution would be to reset the stack inside the splash screen component and redirect the user to the screen that you prefer:
import { NavigationActions } from 'react-navigation'
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Drawer'})
]
})
this.props.navigation.dispatch(resetAction)
For newer versions of react-navigation :
import { StackActions, NavigationActions } from 'react-navigation';
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Profile' })],
});
this.props.navigation.dispatch(resetAction);
Link to the official documentation
One of the simplest solutions would be, replace current screen with new screen so user won't be allowed to go back.
const SplashScreen: () => {
...
navigation.replace("LoginScreen"); // Move forward and Remove this screen from stack
see https://reactnavigation.org/docs/stack-actions/#replace
I solved it using this
code:
const prevGetStateForActionHomeStack = HomeStack.router.getStateForAction;
HomeStack.router = {
...HomeStack.router,
getStateForAction(action, state) {
if (state && action.type === 'ReplaceCurrentScreen') {
const routes = state.routes.slice(0, state.routes.length - 1);
routes.push(action);
return {
...state,
routes,
index: routes.length - 1,
};
}
return prevGetStateForActionHomeStack(action, state);
},
};
replaceScreen = () => {
const { locations, position } = this.props.navigation.state.params;
this.props.navigation.dispatch({
key: 'NearMeMap',
type: 'ReplaceCurrentScreen',
routeName: 'NearMeMap',
params: { locations, position },
});
};
Also if you need in-depth explanation of the code, watch this short video here
In react-navigation 5.x version. If you want to open new screen and remove previous stack screen, you can write: -
navigation.dispatch(StackActions.replace('screen_name', {params: {}}));
The replace action allows to replace a route in the navigation state. You can import it as:-
import { StackActions } from '#react-navigation/native';
If you want to perform other actions on stack navigation check out this link-
https://reactnavigation.org/docs/stack-actions
useEffect(() => {
setTimeout(() => {
navigation.reset({
index:0,
routes:[
{
name:"Welcome",
// params:{key:'param'},
},
],
});
}, 1000);},[]);
You can also trigger the this onPress like:
onPress={()=>navigation.reset({
index:0,
routes:[
{
name:"NameOfScreen",
// params:{key:'param'},
},
],
})}