React Native Switch Navigator: goBack() not working - react-native

I have a switch navigator:
export default createSwitchNavigator({
AuthLoading: AuthLoadingScreen,
EditProfile: EditProfileScreen,
Login: LoginScreen,
Register: RegisterScreen,
ForgotPassword: ForgotPasswordScreen,
Main: MainTabNavigator,
MainLoading: MainLoadingScreen,
TripReport: TripReportScreen,
},
{
initialRouteName: 'AuthLoading',
});
When I am on 'Main', a tab navigator, I have a button that navigates me to the 'TripReport' screen:
onPress={() => navigation.navigate('TripReport', {tripReport: tripReport})}
When on the TripReportScreen, I added an event listener for the Android back button to return to the 'Main', but also to the specific Tab:
componentDidMount() {
// Add event listener for the Android back button.
BackHandler.addEventListener("hardwareBackPress", () => {
this.props.navigation.goBack(null);
return true
})
}
The goBack function isn't working. The only way I've gotten it to work is
navigation.navigate('Main)
but this reloads 'Main' instead of going back to the state the screen had.

Yup, This is correct behaviour.
If I had to quote reactnavigation
Optionally provide a key, which specifies the route to go back from. By default, goBack will close the route that it is called from. If the goal is to go back anywhere, without specifying what is getting closed, call .goBack(null); Note that the null parameter is useful in the case of nested StackNavigators to go back on a parent navigator when the child navigator already has only one item in the stack.
This would work only with StackNavigators.
Hope this helps.

Thats because when you call this.props.navigation.goBack on Main, this.props.navigation actually refers to your MainTabNavigator. Thats why your back action did not work.
You need to reach the correct SwitchNavigator in order to dispatch a back action to your SwitchNavigator, check out a few ways suggested on this thread.
Or, you can also create a reference to your switchNavigator, checkout this answer here
https://stackoverflow.com/a/54878858/2963461

Related

How to go back to initial Screen for drawer when opening deeplink with backBehaviour as history?

I'm using a Drawer Navigator as my base with history and have a Home screen and a Notifications screen.
Now for a deeplink myapp://notifications i want to have a history like Home->Notifications so that on pressing physical back btn the user is taken to Home instead of closing the app.
However it seems this does not work with backBehaviour set as history as now Home is never added when opening deeplink.
Is there any way to update the history so that it always has Home as initial screen?
(I have set Home as Initial screen in my linking config but that does not help)
The reset action allows resetting the navigation state to the given state.
Try to run this dispatch if your app opened using the deep link
(Untested) example:
import { CommonActions } from '#react-navigation/native';
navigation.dispatch((state) => {
// Add the home route to the start of the stack
const routes = [{ name: 'Home' }, ...state.routes];
return CommonActions.reset({
...state,
routes,
index: routes.length - 1,
});
});
Read more about reset action

React Navigation: How to fake a history in Stack Navigator?

I use https://reactnavigation.org/ and my app has a register process consisting of several screens:
First, the user has to enter his first name ("FirstNameView")
Second, the user has to enter his last name ("LastNameView")
...
If the user closes the app in the LastNameView and opens it later again, the app sends a request to the backend to fetch the current user data. Depending on this data, the user is navigated to the right screen via the initialRouteName property:
<MainStack.Navigator
initialRouteName="LastNameView"
>...
I have a back button on every screen which does navigation.goBack(), which obviously not works if the user just opens the app again and I direct him to the right View without pushing other screens to it.
One approach that I could think of is removing the navigation.goBack() functionality and replacing it with navigation.navigate("nameOfPreviousView"). However, this has the drawback that native "back" functionality of e.g. Android would not work.
How can I handle a case like this? Can I somehow create a fake history with my initialRouteName approach?
Yes you can create previous route history without navigating there.
In NavigationContainer initialize initialState like
let initialState = initialRoute = {
index: 1, // to load the second screen which is LastNameView
routes: [
{
name: 'FirstNameView',
},
{
name: 'LastNameView',
},
],
};
Like
<NavigationContainer initialState={initialState}> {/* ... */} </NavigationContainer>
To explore more about initialState check description here https://reactnavigation.org/docs/navigation-container/#initialstate

I can't pass parameters using react-navigation in react-native while navigating from a tab navigator screen to a stack navigator screen

I think this is pretty straight forward when it comes to passing between screens of tab navigators but there seems to be a problem while trying to pass parameters from a tab navigator screen to a stack navigator screen in react-native using react-navigation.
I tried this:
onPress={() => {
this.props.navigation.navigate('review', {
aa1: 86,
bb1: 'anything you want here',
});
}}
And this:
onPress={() => this.props.navigation.dispatch(NavigationActions.navigate({ routeName: 'review', params: { aa1: 'x' }, }))}
as the onPress handler of my TouchableOpacity. None of them work. I can navigate but I can't get the params.
Below is how I try to get the parameters in the target stack navigator screen:
const { navigation } = this.props;
//if a is not passed, No a is the default value.
const a = this.props.navigation.getParam('aa1', 'NO a');
const b = navigation.getParam('bb1', 'No b');
Any ideas?
I was able to figure and solve the problem.
Problem was that name of the screen I was trying to navigate to and the name of the stack navigator (name of the stack navigator in the containing/parent tab navigator) that contained that screen was the same. And although navigation was working, the parameters were not being passed as I said in the problem description. Navigation was working because the screen that I was trying to navigate was set as the initial route in the containing stack navigator. Apparently, I was navigating and passing the parameters to the containing stack navigator. Once I changed the name of the stack navigator, the problem was solved.
Use navigation.push instead of navigation.navigate.
Reference: Oficial Docs.
I was facing the same problem, and i saw your own answer. The reason why this happens easily is that you are sending information to the stack, and not to the screens. As you can see in https://reactnavigation.org/docs/nesting-navigators, when you want to pass parameters to another screen inside a stack you need to specify the stack, then the screen and then the parameters, just like that:
navigation.navigate('Root', {
screen: 'Profile',
params: { user: 'jane' },
});
To be honest you can just put the parameters right with the screen, as in:
navigation.navigate('Root', {
screen: 'Profile',
user: 'jane',
});

How to pass data from one component to Navigator and from Navigator to component

I'm actually doing a login system and when my user is logged, the login component has to send data to my main screen but I don't really understand how to do it.
Actually I've this when user is logged :
User.username = data.username;
User.id = responseJson.id;
User.token = responseJson.token;
this.props.navigation.navigate('Main', { User: User });
User is where everything is saved and it works.
Data is sended to Main a Switch Navigates inside an AppContainer :
export default createAppContainer(createSwitchNavigator(
{
Auth: { screen: AuthStack, navigationOptions: { tabBarVisible: false } },
Main: { screen: MainStack },
},
{
initialRouteName: "Auth"
}),
);
so it goes on Mainstack who's a bottomTabNavigator and works like that :
const MainStack = createBottomTabNavigator(
{
Services: {
screen: Home,
navigationOptions: {
tabBarLabel:"Services",
tabBarIcon: ({ tintColor }) => (
<Icon name="home" size={30} color="#0033CC" />
)
},
},
I know it's not complete I've other screens not only Services, just wanted to avoid too long function on paste.
So is that possible to send this data "User" to my Home screen ?
I'm open to any suggestion or answer, that's my first react Native project so there is maybe some mistakes :)
You'll probably want to persist the user data in the app (save it in the storage for when the user leaves the app then enters he stays logged in). That's why react-navigation has the switchNavigator (the one you are using). In order to persist data, you can use the AsyncStorage to save the user data and in the switch navigator constructor check whether the user data is already available in the storage or not then decide whether to navigate to the app or the authentication page. In fact React Navigation provides an example on this: https://reactnavigation.org/docs/en/auth-flow.html . Since you're new to RN, here's an extra tip on managing data: you can use a State in every component that is only for that specific component (which is also reactive), you can use global data (store) shared between all the components in the app (check react redux/flux/mobx), you can share data from parent components to children components by passing data/functions in the props (aka attributes), and you can use the new React context api to use a provider (also like a store shared between parent and children components)
I've made something maybe a bit more understandable :
I get my user data in component login from an external api and it works well. To go from login to home I'm using this :
this.props.navigation.navigate('Main', { User: User})
So it goes on Main who opens Mainstack Screen who's a bottomTabNavigator and if I don't do any mistakes my User data that I've sent is on this Mainstack but I don't know how to get it and how to share it to the other components.
Every component got his own js file, there is only the App container and Authstack/Mainstack that are on the same file.
Hope you understood it !

How to pass parameters to a deep react-navigation Stack Navigator

I have a react-native app using react-navigation. I want to pass some props all along the stack navigator, but not sure what is the right thing to do.
To give more details, my screens are:
ListThingsScreen: List all things.
ShowThingScreen: Show details of the thing.
EditThingScreen: Edit the thing.
They are organized in such way:
const HomeStack = createStackNavigator(
{
ListThings: ListThingsScreen,
ShowThing: ShowThingScreen,
EditThing: EditThingScreen,
},
{
initialRouteName: "ListThings"
}
);
export default createBottomTabNavigator({
HomeStack
});
An app user:
Log in.
See all things (in ListThingsScreen).
Click on one of them, jump to ShowThingScreen to see its details.
Click on a button of the details screen, jump to EditThingScreen to edit the thing.
Once finished editing, jump back to ShowThingScreen.
In ListThingsScreen componentDidMount(), I will make async call to fetch logged in user data such as userId, username, etc. I want all screens in the stack knows about them to behave correctly when show and when edit.
Solutions I can think of:
Pass parameters to routes, as suggested by react-navigation docs. This works, but as I have more screens in the stack, I write too much code like navigation.push("RouteName", {user}) and navigation.getParam("user", "No user").
This is pretty much the only solution I have found. An alternative is to use React context. I like this approach better, however I want to use react-navigation and the concept of Router and Route don't exist in the library.
// ListItemsScreen.js
import { BrowserRouter as Router, Route } from "react-router-dom";
const UserContext = React.createContext();
<UserContext.Provider value={{ user }}>
<Router>
<div className="app-container">
<Route path="/show/:thingId" component={ShowThing} />
<Route path="/edit/:thingId" component={EditThing} />
</div>
</Router>
</UserContext.Provider>
// ShowItemScreen.js
<UserContext.Consumer>
{({user}) => (...)}
</UserContext.Consumer>
Which approach is more "correct"? If it is the 2nd approach, how can I do it with react-navigation?