Reset the TabNavigator history with react navigation - react-native

I have the following structure:
const routeConfiguration = {
Login: { screen: Login },
Home: { screen: TabBar },
};
const stackNavigatorConfiguration = {
headerMode: 'screen',
navigationOptions: {
header: { visible: false }
}
};
export const RootNav = StackNavigator(routeConfiguration, stackNavigatorConfiguration);
My TabBar where each Tab has it's own StackNavigator:
const routeConfiguration = {
TabOneNavigation: { screen: TabOneNavigation },
TabTwoNavigation: { screen: TabTwoNavigation },
TabThreeNavigation: { screen: TabThreeNavigation },
};
const tabBarConfiguration = {
tabBarOptions: {
activeTintColor: 'white',
inactiveTintColor: 'lightgray',
labelStyle: {
fontSize: 10,
fontFamily: Fonts.book
},
style: {
backgroundColor: Colors.greenLightGradient,
borderTopWidth: 1,
borderTopColor: Colors.tabGreenLine
},
}
};
export const TabBar = TabNavigator(routeConfiguration, tabBarConfiguration);
When the app first load It goes to Login screen. After successful login I use actionTypes.TO_HOME to go to Home. There I have 3 tabs (Feed, Suggestion, Profile). Inside Profile tab I have Log Out button pressing on which I reset the navigation history and go to Login Again (so far so good). But when I login again and go to Home, it shows the first tab for a second and navigates me to the last one (Profile) which looks like the TabNavigator's history is not resetted. Should I do something special so that the TabNavigator's history is also resetted?
This is my reducer for resetting the history and going to the Login screen:
export const navReducer = (state = initialState, action = {}) => {
let nextState;
switch (action.type) {
case actionTypes.TO_LOGIN:
nextState = RootNav.router.getStateForAction(
NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ type: NavigationActions.NAVIGATE, routeName: actionTypes.TO_LOGIN })],
key: null
}), state);
break;
case actionTypes.TO_HOME:
nextState = RootNav.router.getStateForAction(
NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ type: NavigationActions.NAVIGATE, routeName: actionTypes.TO_HOME })],
}), state);
break;
default:
nextState = RootNav.router.getStateForAction(action, state);
break;
}
return nextState || state;
};

You can put the index 0 on navigate action

This is an React-Navigation v5 Example, to clear all History:
navigation.dispatch((state) => {
return CommonActions.reset({
...state,
history: state.history
? [
state.history[
state.history.length - 1
],
]
: undefined,
});
});

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 ?

Modal navigation for certain screens in react-native app

For 3 screens in my app I need to make a modal transition.
For the rest of the screens - I need the default right-to-left transition.
How do I achieve this with different stack navigators?
const AuthStack = createStackNavigator(
{
LogIn: { screen: LogInScreen },
Signup: { screen: SingupScreen },
Welcome: { screen: WelcomeScreen },
CodeVerification: { screen: CodeVerificationScreen },
PasswordSelection: { screen: PasswordSelectionScreen },
RegistrationSuccess: { screen: RegistrationSuccessScreen }
},
{
initialRouteName: 'LogIn'
}
)
const ModalNavigator = createStackNavigator({
ContactInfoEdit: { screen: ContactInfoEditScreen },
DeliveryAddressEdit: { screen: DeliveryAddressEditScreen },
OrderPlacedScreen: { screen: OrderPlacedScreen },
},
{
initialRouteName: 'ContactInfoEdit',
})
const ProductsStack = createStackNavigator(
{
Products: {
screen: ProductsScreen
},
Product: {
screen: ProductScreen
},
ProductBuy: {
screen: ProductBuyScreen
},
OrderConfirm: {
screen: OrderConfirmScreen
},
PlaceOrder: {
screen: PlaceOrderScreen
},
Modal: ModalNavigator
},
{
initialRouteName: 'Products',
mode: 'modal',
}
)
If I set mode: modal it will make all the navigation animations will be modal.
If I remove it, all the navigations will be default (right-to-left)
const ProductsTabStack = createBottomTabNavigator(
{
Orders: { screen: OrdersScreen },
Products: { screen: ProductsStack },
Profile: { screen: ProfileScreen }
},
{
initialRouteName: 'Products',
backBehavior: 'none',
tabBarOptions: {
activeTintColor: '#ffffff',
inactiveTintColor: primaryColor,
activeBackgroundColor: primaryColor,
labelStyle: {
marginBottom: 17,
fontSize: 15,
},
tabStyle: {
shadowColor: primaryColor,
borderWidth: 0.5,
borderColor: primaryColor,
},
},
},
)
export const AppNavigator = createSwitchNavigator({
Auth: AuthStack,
Categories: ProductsTabStack
})
I tried setting mode: modal in the ModalNavigator, but then it took the default parent navigation.
You probably want to try to use StackNavigatorConfig while navigating to that screen this.props.navigation.navigate('ScreenName', params, {mode: 'modal'})
If you want to keep all your transition code in same file as you have right now, you can do the same as what react-navigation is suggesting here
It goes something like that
import { createStackNavigator, StackViewTransitionConfigs } from 'react- navigation';
/* The screens you add to IOS_MODAL_ROUTES will have the modal transition. */
const IOS_MODAL_ROUTES = ['OptionsScreen'];
let dynamicModalTransition = (transitionProps, prevTransitionProps) => {
const isModal = IOS_MODAL_ROUTES.some(
screenName =>
screenName === transitionProps.scene.route.routeName ||
(prevTransitionProps && screenName ===
prevTransitionProps.scene.route.routeName)
)
return StackViewTransitionConfigs.defaultTransitionConfig(
transitionProps,
prevTransitionProps,
isModal
);
};
const HomeStack = createStackNavigator(
{ DetailScreen, HomeScreen, OptionsScreen },
{ initialRouteName: 'HomeScreen', transitionConfig: dynamicModalTransition }
);
OK, found a workaround for custom transitions in 1 StackNavigator using https://www.npmjs.com/package/react-navigation-transitions:
const handleCustomTransition = ({ scenes }) => {
const nextScene = scenes[scenes.length - 1]
if (nextScene.route.routeName === 'ContactInfoEdit')
return fromBottom()
else
return fromRight()
}
const ProductsStack = createStackNavigator(
{
Products: {
screen: ProductsScreen
},
Product: {
screen: ProductScreen
},
ProductBuy: {
screen: ProductBuyScreen
},
OrderConfirm: {
screen: OrderConfirmScreen
},
PlaceOrder: {
screen: PlaceOrderScreen
},
ContactInfoEdit: { screen: ContactInfoEditScreen },
DeliveryAddressEdit: { screen: DeliveryAddressEditScreen },
OrderPlacedScreen: { screen: OrderPlacedScreen },
},
{
initialRouteName: 'Products',
transitionConfig: (nav) => handleCustomTransition(nav)
}
)

react-navigation 3 reset in nested stack

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.

How to add or remove tab item in bottom tab?

I have issue with react-navigation v3. I want to add new component (tab item) or remove exists tab item in the current tab bar.
I have do:
export const MainTabContainer = createBottomTabNavigator(
{
Genre: {
screen: GenreScreen
},
History: {
screen: HistoryScreen
}
},
{
initialRouteName: 'Genre',
backBehavior: true,
lazy: true,
tabBarOptions: {
activeTintColor: '#454545',
activeBackgroundColor: 'white',
inactiveTintColor: '#ccc',
inactiveBackgroundColor: 'white'
}
}
);
const defaultTabGetStateForAction = MainTabContainer.router.getStateForAction;
const defaultTabGetComponentForRouteName = MainTabContainer.router.getComponentForRouteName;
MainTabContainer.router.getStateForAction = (action, state) => {
if (!state || !action || !action.name) {
return defaultTabGetStateForAction(action, state);
}
if (action.name === 'addTab') {
const routes = [
...state.routes,
{key: action.key, routeName: action.routerName, params: action.data}
];
return {
...state,
routes,
index: routes.length - 1,
};
}
return defaultTabGetStateForAction(action, state);
};
MainTabContainer.router.getComponentForRouteName = (routeName) => {
if (routeName === 'Recent') return RecentScreen;
else if (routeName === 'About') return AboutScreen;
return defaultTabGetComponentForRouteName(routeName);
};
export const Home = createDrawerNavigator(
{
Main: MainTabContainer
},
{
drawerPosition: 'left',
useNativeAnimations: true,
drawerType: 'front',
contentComponent: MenuDrawer
}
);
const RootStack = createStackNavigator(
{
SplashScreen: SplashScreen,
Home: Home,
DetailScreen: DetailScreen
},
{
initialRouteName: 'SplashScreen',
mode: 'modal',
headerMode: 'none',
header: null,
transparentCard: true,
cardStyle: {
shadowColor: 'transparent',
backgroundColor:"transparent",
opacity: 1
}
}
);
export const AppContainer = createAppContainer(RootStack);
This way will return error not found the routerName GenreScreen or AboutScreen.
When the user have done something, I want to add GenreScreen or AboutScreen or remove one of them on it. GenreScreen and AboutScreen is not display normally.
Have any suggest to solve this issue?
The first, thank for time.