Why Navigation.goBack() not working properly? - react-native

I am using following way to create navigation system
StackNavigator -> DrawerNavigator -> (Screen A, Screen B, Screen C)
Screen A is initial Route
Screen A to Screen B Using
this.props.navigation.navigate("Screen B") //Working Fine
Screen B to Screen C Using
this.props.navigation.navigate("Screen C") //Working Fine
In Screen C
this.props.navigation.goBack() //Not Working
But
this.props.navigation.goBack(null) //It's going to Screen A instead of Screen B
What's wrong here.
Please help me Thank you.

As you didn't show the navigation structure of the drawer, I don't know how the 3 screens are put inside of it. I'm assuming they are 3 different drawerScreens
This happens because you are using a DrawerNavigation, that doesn't create a history of screens when you navigate around it.
To solve this you have to change the navigation structure to something like:
DrawerNavigation => StackNavigator => Screen A, B, C

You can specify backBehavior="history" inside Drawer.Navigator and it shouldn't move you to the initial root on goBack anymore :)
Example:
<Drawer.Navigator
backBehavior="history"
>
...

As the answer above is, the drawer navigators are not in the stack, so you can't tell the path of your journey.
the key property for goBack() is a dynamically created string, created by react-navigation whenever navigate to a new route.
These stack keys are stored in this.props.navigation.state
NOTE: Use .goBack(null) if your goal is to return to any place
without specifying a closing target.
Like this example, you can add to the stack.
Example
const Drawers = createDrawerNavigator(
{
A: {
screen: HomeScreen
},
...
},
{
initialRouteName: "A"
}
);
const RootStack = createStackNavigator(
{
Drawers: {
screen: Drawers
},
otherStack: {
screen: otherStack
},
....

Related

React - Navigating to the same component weird behaviour

I'm breaking my head over this, the behaviour I'm seeing seems weird for me, though it might make sense to some of you.
Consider this component:
const DishPreparation = ({ dish }) => {
const [slideIndex, setSlideIndex] = useState(0)
const sceneRef = useRef(null)
useKeepAwake();
return (
<View style={styles.scene} ref={sceneRef}>
<View flex={0.12} style={{ marginTop:-10 }}>
<ProgressSteps activeStep={slideIndex} activeStepIconBorderColor={colors.lightTan} completedProgressBarColor={colors.lightTan} completedStepIconColor={colors.lightTan}>
{dish.preparationSteps.map((step, index) => (
<ProgressStep removeBtnRow key={index}/> ))}
</ProgressSteps>
......
)
Which is being rendered through another component Dish, that can be navigated to. I have some floating component in my app that allows me to navigate to Dish with a specific dish. If I navigate to a dish through it for the first time, or if I navigate there through other component that doing it by pushing the component to the stack, everything works fine. The weird behaviour is when I'm already inside this component with a specific dish, and then navigate through the floating component to a different dish, it's like the old dish is being kept.
Lets say first dish had 3 elements in dish.preparationSteps, and the second one has 4, then dish.preparationSteps.map(step, index) returns only 3 elements instead of 4, but if I render step.someInfo inside the mapping, then I actually see the new dish values.
How is this happening? I'd expect either 4 elements to be returned, or 3 elements but with the old dish values, how is this mixture happening? Also, dont know if it helps but slideIndex keeps its old value, and doesn't reinitialize to 0.
This is how I navigate through the floating component:
navigation.navigate('Tabs', {
screen: 'Home',
params: { screen: 'Dish', params: { from: '', data: dish } },
})
This is how I navigate to it through other some other component(which works as expected)
navigation.push('Dish', {from: 'DishList', data: item})
If any other code is needed I'll be happy to add it.
When using .navigate instead of .push, navigation will look for that screen name ("Dish") and just change navigation params, without remounting component or opening a new screen with it. Usually you can just use navigation.push (like in your last example), but the problem is that you are trying to navigate from some top-level navigator. You can't use .push there because it will push to the outer navigator which doesn't have "Dish" screen. If you want to add another "Dish" screen on top of the existing one in the nested navigator, you need to navigation.push from the Stack Navigator you want to navigate in; it's only that your floating is not located in that Stack Navigator
First thing you can try is to add a unique key to your screen when navigating, e.g.
navigation.navigate('Tabs', {
screen: 'Home',
params: { screen: 'Dish', key: dish.id, params: { from: '', data: dish } },
})
so that navigation will compare screens not by the name but by the name and key. This will most likely replace existing Dish screen with a new one
Better solution would be to dispatch navigation action that will drill down to the Stack Navigator you want to navigate in, and dispatch StackActions.push there
And even better solution would be not trying to navigate inside inner navigators from outer navigators
Maybe the getId prop is what you're looking for?
From the React Navigation docs:
In a stack navigator, calling navigate with a screen name will result in different behavior based on if the screen is already present or not. If the screen is already present in the stack's history, it'll go back to that screen and remove any screens after that. If the screen is not present, it'll push a new screen.
You could use the getId prop to push a new screen instead. For example, say you have specified a getId prop for Profile screen:
<Screen name={Profile} component={ProfileScreen} getId={({ params }) => params.userId} />
Now, if you have a stack with the history Home > Profile (userId: bob) > Settings and you call navigate(Profile, { userId: 'alice' }) the resulting screens will be Home > Profile (userId: bob) > Settings > Profile (userId: alice) since it'll add a new Profile screen as no matching screen was found.

How to goBack globally between stacks in React Navigation?

I am using react navigation ("#react-navigation/native": "^5.1.3") and I have the following setup:
BottomNavigation
-stack1
-stack2
It looks like goBack() is local to the stack. What that means is that if I navigate from a page in stack1 to a page in stack2, I am unable to go the the page I came up from.
Solutions (or rather hacks) that didn't work for me:
pass the source screen as param and then navigate. That isn't a real back button and does not play well with android back button.
Put all pages in bottom navigation. Bottom navigation does not have animations it seems, so I can not achieve the right transitions
Put all pages in stack navigation. In this case I lose the fixed bottom navigation. I can add it to each page, but when transitioning it will go away with the old screen and come again with the new one, which is undesirable.
So I am wondering if I am missing something big here, like a globalBack() that I overlooked?!
And also, I am looking for a solution to this problem which remains.
Naturally if you have bottoms tabs with each tab having its own stack navigator, calling navigation.goBack() will go back from one screen inside stack navigator to previous screen inside that same stack navigator. That's how navigation works in pretty much every app. Pressing back button or swiping back does not change tab for you, tabs are more like separate small apps by itself. If you want to specifically jump from one tab to another instead of going back in stack, use navigation.dispatch(TabActions.jumpTo('Profile')). If pressing something inside tab#1 makes you go to to tab#2 then this screen most likely also belongs to tab#1
also, take a look at backBehavior prop of Tab.Navigator, it might be doing what you want depending on what exactly it is you want https://reactnavigation.org/docs/bottom-tab-navigator/#backbehavior
I'm using bottom tab navigator with 2 stacks as well. I faced similar issue and agree with #Max explanation. Due to my Notification screen is in Stack 1, I have to goBack to Notification after navigating away to Detail screen. After searching for the fix, I'm using this workaround (for v6).
Tab 1 - Stack 1 (Home > Notification screen)
Tab 2 - Stack 2 (Reward Home > Reward Detail screen)
I passed a param when navigating from Notification to RewardDetail. Then I override the headerLeft and use BackHandler to handle Android back function.
Notification.js
navigation.navigate('RewardStack', {
screen: 'RewardDetail',
initial: false,
params:{notification: notification_data_source}
})
RewardDetail.js
const payload = route.params.notification
//1. override headerLeft button
useLayoutEffect(() => {
if(payload)
navigation.setOptions({
headerLeft: () => (
<Button
TouchableComponent={TouchableOpacity}
buttonStyle={{paddingTop:4, paddingLeft:0}}
type='clear'
icon={<Icon name={'chevron-left'} size={30} style={{color:'#FFF'}} />}
onPress={()=>{
navigation.goBack()
navigation.navigate('Notification') //can use this only
}}
/>
)
})
}, [navigation]);
//2. Add BackHandler
useEffect(() => {
const onBackPress = () => {
if (payload) {
navigation.goBack()
navigation.navigate('Notification') //can use this only
return true
} else {
return false
}
}
BackHandler.addEventListener('hardwareBackPress', onBackPress)
return () => BackHandler.removeEventListener('hardwareBackPress', onBackPress)
}, [navigation]);
I can just use navigation.navigate('Notification') to return to Notification but this will cause Detail screen to stay mounted in Stack 2. I want the Stack 2 to return to RewardHome after go back to Notification. Hence I used:
navigation.goBack() //pop screen to RewardHome
navigation.navigate('Notification') //jump to Notification

Conditional React Navigation header button

I am trying to make a button appear in headerRight of the header bar in a React Navigation screen, but I cant seem to get it to work.
I want the start button (what is in headerRight) to show when props.players.length > 1.
Can anyone point me in the right direction?
You can use the same mechanics describe here for title: https://reactnavigation.org/docs/en/headers.html#setting-the-header-title
Set a navigation params and use it on your navigationOptions.
In your case:
state = { players: 0 };
static navigationOptions = ({ navigation }) => {
return {
headerRight: navigation.getParam('players', 0) > 1 ? <YourHeaderButtonComponent /> : null ,
};
};
addPlayer = () => {
this.setState(({players}) => {
this.props.navigation.setParams({players: players + 1})
return {players: players + 1 }
});
}
render {
...
<Button onPress={this.addPlayer}
...
}
If you have a nested navigation for example a child bottomTabNavigator inside a parent stackNavigator please look att applying parent screen options based on child navigator's state see documentation here
For me the second option with Using navigation.setOptions worked with a child bottom tab navigator so I could change stackNavigator header options based on bottomTabNavigator page.

React Navigation cache component

I am fairly new to React Native and a little confused with the following. I've set up a StackNavigator as shown
const MyProjectNavigator = StackNavigator({
home: {screen: Other},
latest_news: {screen: LatestNews}
}
When I want to go to a different screenm the navigation I perform is:
navigate(
latest_news, {
otherParams : param1
}
);
This works well so far.
Now, assume that the latest_news component queries a lot of data from a server when mounted, then performs lots of operations on that data, sorting by date, author, yadda yadda. This takes some time to complete.
How would you suggest I made this faster? On iOS for example i would normally keep my ViewController in memory and if it was available, display that. When using navigate(), the navigator seems to create a new instance of the component thus reloading and re-processing everything from the beginning making the users wait every time.
*TL;DR
I want to keep all the data my component has queried and processed across navigation so that the processing doesn't have to repeat constantly.
I could just put the data on the global object but that doesn't sound like a good solution
Thanks a bunch.
You can use the TabNavigator instead of the StackNavigator.
TabNavigators reuse the same instances of its route items.
you can disable the visibility of the TabBar:
const TabNav = TabNavigator({
Home: { screen: HomePage},
NewsFeed1: { screen: NewsFeed} ,
NewsFeed2: { screen: NewsFeed} ,
NewsFeed3: { screen: NewsFeed} ,
// ...
},
{
navigationOptions: ({ navigation }) => ({
tabBarVisible: false,
}),});
and then you can navigate manually - for example with a button:
<Button title="NewsFeed1" onPress={() => this.props.navigation.navigate("NewsFeed1") }/>
Have a look at a simple example:
https://github.com/chrisdie/AwesomeNavigation/blob/master/src/tabbar2/App.js

go two screen back with single press event using react-navigation in react native app

I am using reactnavigation component from https://reactnavigation.org and using the code below i am going one screen back
<Button
onPress={() => goBack()}
title="Go back from this HomeScreen"
/>
how can i go 2 screen back on single press action
I am using this code to initialize the navigator
const RouteConfigs = {
Login: {screen:Login},
Home: {screen:Home},
Chat: {screen:Chat},
Facebook: {screen:Facebook},
Facebookgallery: {screen:Facebookgallery}
}
const StackNavigatorConfig = {
headerMode: 'none',
}
export default StackNavigator(RouteConfigs, StackNavigatorConfig)
I navigate from home to Facebook with this code :
() => this.props.navigation.navigate('Facebook', {user:this.state.user})
and from Facebook to Facebookgallery with this code :
onPress={ () => this.props.navigation.navigate('Facebookgallery', {user:this.state.user}) }
now i want to go back from Facebookgallery to Home directly with some parameters
I know it's an older question but what I use is:
navigation.pop(n);
Takes you to the previous screen in the stack. If you provide a number, n, it will specify how many screens to take you back within the stack.
https://reactnavigation.org/docs/en/navigation-prop.html
you can use navigation.pop(screenCount)
with screenCount as integer
navigation.pop(2)
here is for refference
https://reactnavigation.org/docs/stack-actions#pop
React Navigation is updated not to use pop function directly, use dispatch function with StackActions.
navigation.dispatch(StackActions.pop(2));
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_A });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_B });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_C });
navigation.navigate({ routeName: SCREEN, key: SCREEN_KEY_D });
Now you are on-screen D and want to go back to screen A (popping D, C, and B). Then you need to supply a key to goBack FROM:
navigation.goBack(SCREEN_KEY_B)
https://reactnavigation.org/docs/en/navigation-prop.html#going-back-from-a-specific-screen-with-goback
The "proper" solution right now is:
Get the key for the screen after the screen you want to go to. In
your case, you'll need to get the key for the Facebook screen.
Call NavigationAction.back(key: 'key-of-previous-screen'), and it
will pop your navigation stack as if you were on the screen with that key
Code example where I go back multiple screens in a thunk (relevant if you use redux):
export const postItemAndReturnToMap = item => (dispatch, getState) => {
const {nav} = getState();
// Get the key for the screen after/above the root view and use that as reference
// to return to the root view. This is hardcoded for my stack setup, as you can see
// there are quite a few nested StackNavigators in my setup
const {key} = nav.routes[0].routes[0].routes[0].routes[1];
// Dispatch whatever action you want
dispatch(postItem(item));
// This will now go back multiple screens, in my case to the
// bottom of the top stackNavigator
return dispatch(NavigationActions.back({key}));
};
This isn't pretty. Let's hope react-navigation implements something like .back(2) to make things easier in the future.
If you are using push then you can go to root screen with this
this.props.navigation.popToTop();
and to Push
this.props.navigation.navigate('SummaryScreen', {
otherParam: 'anything you want here',
});
You could define an additional navigation action type, e.g. POP_TWO_ROUTES and overwrite StackRouter.getStateForAction(passedAction, state) like that (only exemplary):
...
const defaultGetStateForAction = AppNavigator.router.getStateForAction;
AppNavigator.router.getStateForAction = (passedAction, state) => {
if(state && state.routes && state.routes.length > 2
&& passedAction.type === 'POP_TWO_ROUTES') {
let routes = state.routes.slice();
routes.pop();
routes.pop();
return {
index: routes.length - 1,
routes: routes
};
}
// default behaviour for none custom types
return defaultGetStateForAction(passedAction, state);
}
...
Then in your screen component you can do something like that:
...
onPress={() => this.props.navigation.dispatch({
type: 'POP_TWO_ROUTES'
})}
...
See also https://reactnavigation.org/docs/routers/#Custom-Navigation-Actions.
It sounds like you're looking for something along the lines of #reset (https://reactnavigation.org/docs/navigators/navigation-actions#Reset).
I haven't used this library, but you likely are trying to go back to the navigation stack's root screen. Other libraries I've used for nav have some sort of popToRoot or something similar.
Try this
onPress={() => {
props.rootNavigation.dispatch(
NavigationActions.reset({
index: 0,
key:null,
actions: [NavigationActions.navigate({ routeName: 'Home' })]
})
)
}}
This should work:
this.props.navigation.pop(2)
For React Native V6 2022
Simple Go Back "goBack"
navigation.goBack()
Go Back tow screen or 3 on specific the name "navigate"
props.navigation.navigate("RouteName")
Navigate to the top screen "popToTop"
navigation.popToTop()