react navigation how to go back to different stack - react-native

const HomeStack = createStackNavigator({
Home: HomeScreen,
});
const CarStack = createStackNavigator({
Booking: BookingScreen,
Confirm: ConfirmScreen
});
export default createBottomTabNavigator({
HomeStack,
CarStack,
});
export default class HomeScreen extends React.Component {
goTo(){
this.props.navigation.navigate('Confirm')
}
render() {
return (
<View style={css.container}>
<button onPress={this.goTo.bind(this)}>go to</button>
</View>
);
}
}
export default class ConfirmScreen extends React.Component {
goBack(){
this.props.navigation.goBack()
}
render() {
return (
<View style={css.container}>
<button onPress={this.goBack.bind(this)}>goback</button>
</View>
);
}
}
In home screen, when I click goto, it take me to confirmscreen, which is good.
But when I press go back, it takes me to booking screen instead of home screen. Which is bad.
It suppose to take me to home screen. What am i missing here?

You have to define 2 separate navigators and check user/guest in another component. like this:
Provider.js:
export default class Provider extends Component {
render() {
let {key, signedIn, checkedSignIn} = this.state
return(
<View>
{
checkedSignIn
? signedIn
? <App />
: <SignedOut />
: null
}
</View>
)
}
}
App.js:
export default createStackNavigator({
home: Home // sample
})
SignedOut.js:
export default createStackNavigator({
auth: Auth // sample
})

If you are using react-navigation v2.x or greater, directly use the navigate method to go to your Home screen.
Customize the back button behaviour and call this.props.navigation.navigate('Home') on the back button press in Confirmation screen. This will cause the desired effect.
Since react-navigation v2.0, the navigate action is less pushy. If the route you specify is already in the stack, then that screen will be opened, instead of pushing a new screen in stack by default.
Reference

Try this:
this.props.navigtion.goBack('Home')
I hope it help you.

Related

React-Navigation passing functions to screenProps

I am creating an app using React Native, Expo and React Navigation, and I don't know the correct method for passing props and functions around.
I have copied the Expo method for navigators that I will build on, but right now I just have App.js calling the following
AppNavigator -> MainSwitchNavigator -> HomeScreen
I have then wrapped the main exported App that Expo expects with the Amazon AWS Amplify HOC withAuthenticator. Now I can log in to my app and show hello world securely using Amazon's cognito service.
If I want to log out I can use the signout function bellow that I currently have on the props of App.
class App extends React.Component {
constructor(props) {
super(props);
this.signOut = this.signOut.bind(this);
}
signOut() {
Auth.signOut().then(() => {
this.props.onStateChange('signedOut', null);
console.log("signed out");
}).catch(e => {
console.log(e);
});
}
render () {
return (
<View style={styles.container}>
<AppNavigator screenProps={{
signOut: () => {this.signOut}
}}/>
</View>
);
}
}
export default withAuthenticator(App)
For now I just want to pass this function down to my Home Screen so I can add a button and log out.
AppNavigator
export default createAppContainer(
createSwitchNavigator({
// You could add another route here for authentication.
// Read more at https://reactnavigation.org/docs/en/auth-flow.html
Main: { screen: MainSwitchNavigator, params: { signOut: this.props.screenProps.signOut } },
},
{
initialRouteName: 'Main'
})
);
MainSwitchNavigator
export default createSwitchNavigator({
// Home: { screen: HomeScreen }
Home: { screen: HomeScreen, params: { signOut: this.props.navigation.params.signOut } }
},{
initialRouteName: 'Home'
});
HomeScreen
class HomeScreen extends Component {
render () {
return (
<View>
<Text>Main App Here</Text>
<Button
onPress={this.props.navigation.params.signOut()}
title="Sign Out"
/>
</View>
)};
}
export default HomeScreen;
At the moment I get the error
undefined is not an object (evaluating 'this.props.navigation')
<unknown>
MainSwitchNavigator.js
8:62
Which puts its at the point I'm trying to read the props passed in to MainSwitchNavigator.
So my question is, what is good practice on sharing functions and state with screens below the main App and how do I pass the signOut function down to the rest of my components?
=======================================================================
EDIT
I have since worked out how to use screenProps correctly for variables but not for functions. screenProps is passed down through navigators automatically to the screen. So I only have to pass it to the AppNavigator component once and I can access it in HomeScreen. However any functions are not passed down.
E.g for variables, if I modify App.js to pass text to the variable signOut and pass that to screenProps
class App extends React.Component {
constructor(props) {
super(props);
this.signOut = this.signOut.bind(this);
}
signOut() {
Auth.signOut().then(() => {
this.props.onStateChange('signedOut', null);
console.log("signed out");
}).catch(e => {
console.log(e);
});
}
render () {
return (
<View style={styles.container}>
<AppNavigator screenProps={{signOut: "some text"}}/>
</View>
);
}
}
HomeScreen will then show this as part of the this.props object.
class HomeScreen extends Component {
render () {
return (
<View>
<Text>Main App Here</Text>
<Button
onPress={console.log(JSON.stringify(this.props))}
// onPress={alert(this.props.screenProps.var3)}
title="Sign Out"
/>
</View>
)};
}
export default HomeScreen;
However if I pass a function to AppNavigator instead and do this
<AppNavigator screenProps={{signOut: () => {this.signOut}}}/>
screenProps does not get set and I can't access the function signOut.
The only thing I can get to work is this
<AppNavigator screenProps={this.signOut}/>
But what I need is to create a property of screenProps and pass this down. Any thoughts?
passing a function as a screenProp should be
<AppNavigator screenProps={{signOut: this.signOut}} />
then to use do
this.props.screenProps.signOut()
MainSwitchNavigator
export default createSwitchNavigator({
// Home: { screen: HomeScreen }
Home: { screen: HomeScreen }
},{
initialRouteName: 'Home'
});
to send data on navigate
this.props.navigation.navigate('Signout' , { name: 'John Doe', email: 'john#doe.com' })

React native react-navigation drawer not opening

I am using react-navigation as the navigation package for my react native application. and have also installed and configured react-native-gesture-handler along with react-navigation as mentioned in the documentation.
The problem i am facing is that the drawer doesn't open at random times. mostly this occurs when user goes through along the main stack navigation and comes back to home to open the drawer. otherwise the drawer seems to be working without any issues.
this is how i have configured my navigation,
MAIN STACK NAVIGATION
const AppStack = createStackNavigator(
{
DrawerNav: DrawerNav,
Home: Home,
Notification: Notification,
HomeSearch: HomeSearch
}
DRAWER NAVIGATION
const MyDrawerNavigator = createDrawerNavigator(
{
Home: Home,
MyAccount: MyAccount,
ContactUs: ContactUs,
InviteFriend: InviteFriend,
Terms: Terms,
SignOut: SignOut
},
And the MAIN STACK also contains a few TAB STACK also,
I want to know why the drawer doesn't respond.
The Code i used to open the drawer was
this.props.navigation.openDrawer();
bu the above code gave
this.props.navigation.openDrawer() undefined
when ever the above crash i mentioned occurs
as a fix i used,
import { DrawerActions } from "react-navigation";
this.props.navigation.dispatch(DrawerActions.openDrawer())
the above code also stop working after a the user goes through the STACK navigation a few times, but doesn't give any errors on development.
This error occurs both on production as well as development
currently running
react native : 0.59.8
react : 16.8.3
react navigation: 3.9.1,
react-native-gesture-handler:1.1.0,
any help would be much appreciated,
Thanks in advance
Try wrapping all your stack navigation with Drawer navigation.
const StackNav = createStackNavigator({
Home: Home,
Notification: Notification,
HomeSearch: HomeSearch
}
Now wrap the above with Drawer navigation
const AppStack = createDrawerNavigator({
StackNav: {
screen: StackNav,
navigationOptions: {
drawerLabel: <Hidden />,
},
},
Home: Home,
MyAccount: MyAccount,
ContactUs: ContactUs,
InviteFriend: InviteFriend,
Terms: Terms,
SignOut: SignOut
});
Now the StackNav will be showing in the Drawer as one of the screens. So create a class and return null then pass it down to Drawer label.
class Hidden extends Component {
render() {
return null;
}
}
Now you'll be able to call the this.props.navigation.openDrawer(); anywhere on the app. Let me know if it works.
I think you can use another easy way to handle this problem :
You can use react-native-drawer that is available in this link now i'm going to show you how you can work with it :
AppStack :
const AppStack = createStackNavigator(
{
Home: Home,
Notification: Notification,
HomeSearch: HomeSearch
}
Home Navigation
const MyHomeNavigator = createStackNavigator(
{
Home: Home,
MyAccount: MyAccount,
ContactUs: ContactUs,
InviteFriend: InviteFriend,
Terms: Terms,
SignOut: SignOut
},
Now lets assume this is your HomePage :
HomePage
import Drawer from 'react-native-drawer'
import DrawerComponent from '../components/drawer'
export default class HomePage extends Component{
render() {
return (
<Drawer ref={(ref) => this._drawer = ref} content={<DrawerComponent {...this.props}/>} side='right' captureGestures openDrawerOffset={0.3} acceptTap>
//your home page components
</Drawer>
)
}
}
as you can see you can access to the drawer by this._drawer and here i will show you how does <DrawerComponent> like :
DrawerComponent :
export default class DrawerComponent extends React.Component {
navigateTo = (path) => {
this.props.navigation.navigate(path)
}
render(){
return(
<View>
<View>
<Item path='MyAccount' navigate = {this.navigateTo}/>
<Item path='ContactUs' navigate = {this.navigateTo}/>
<Item path='InviteFriend' navigate = {this.navigateTo}/>
//And you add all the item you need here with navigateTo function
</View>
<View>
//LogOut Section
</View>
</View>
)
}
}
This Works for me fine, I hope this works for you too.
Best regards .

Can a stack navigator have 2 different routes in react navigation

My project structure looks like this :
Tab navigator
|-Tab1
|-Tab2
|-stack navigator
|-login
|
screen1
|
screen2
|
screen3
|-Tab3
|-Tab4
I want my stack navigator route to start from screen1 once I enter the username and password for login screen. Is there any way we can achieve this?
Can we reroute/change the routes of stack navigator?
Use SwitchNavigator to change/reroute the screen stack
I'm not using React Navigation V2, you should check the doc if you need more information about RNV2
Here's an example TODO:
in router.js (handling screen):
const Tab = TabNavigator({
Customer : {
screen: Customer,
}
});
const TabLogged = TabNavigator({
Handyman: {
screen: Handyman,
}
});
// here's the key to handle which account is logged
export const Check = SwitchNavigator({
Auth: Auth,
Account1: Tab,
Account2: TabLogged
})
create Auth.js to check which account is logged or not:
class foo extends React.component{
componentWillMount(){
this.props.navigation.navigate(usertype == "isLoggedIn" ? 'Account2' : 'Account1');
}
render(){
<View>
<ActivityIndicator size="large" color="#0000ff" />
<StatusBar barStyle="default" />
</View>
}
}
and in your app.js or index.js :
class app extends Component{
render() {
return <check/>;
}
}

React Navigation - How to pass data across different screens in TabNavigator?

I have a TabNavigator, and in each tab is a StackNavigator. Inside the StackNavigator, I have screens. The screens in each Tab do not call each other directly; the TabNavigator handles the screen changes when a tab is pressed.
In the first tab, if the user clicks a button, some data is created. If the user then navigates to the second Tab, I would like to pass this data to the screen in the second Tab.
Here is a demo of the code:
import React from 'react';
import { Button, Text, View } from 'react-native';
import {
createBottomTabNavigator,
createStackNavigator,
} from 'react-navigation';
class HomeScreen extends React.Component {
doIt = () => {
this.props.navigation.setParams({results: ['one', 'two']}); // <--- set data when user clicks button.
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/* other code from before here */}
<Button
title="Set Results"
onPress={this.doIt}
/>
</View>
);
}
}
class SettingsScreen extends React.Component {
render() {
console.log(this.props.navigation); // <--- console out when user clicks on this tab
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings</Text>
</View>
);
}
}
const HomeStack = createStackNavigator({
Home: HomeScreen,
});
const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
});
export default createBottomTabNavigator(
{
Home: HomeStack,
Settings: SettingsStack,
},
{
}
);
The this.props.navigation.state.params never gets the data results in the second Tab. There isn't even a key for it, so if I try to access this.props.navigation.state.params.results, it will be undefined.
This is confusing because I thought props.navigation is passed to all screens automatically.
How can I pass data from one screen to another through the TabNavigator, using just react-navigation? I have seen answers that say to use Redux, but I would not like to import another library if all I want is to keep some state across screens in different react navigators.
It may seem that this.props.navigation.state.params is only able to old one parameter? Possibly? Try this:
doIt = () => {
this.props.navigation.setParams({results: 'one'}); // <--- set data when user clicks button.
}
console.log(this.props.navigation.state.params.results);
Setting props did not work when passing data across different tabs. I even tried playing with AsyncStorage, trying to save and retrieve them in different tabs.
I ended up using Redux to save my states, and that has worked well so far.
I came across a similar problem. I had a multi page form that the client insisted on having each step be enclosed in a tab on a tab bar. I used the react navigation createMaterialTopTabNavigator to create the navigator and couldn't find an easy way to pass the form data between tabs.
What I end up doing was using react's Context API and wrapped the tab navigator in a root form container that provides the context value to the navigator and routes inside. Here is how I did it:
Root form container
// MultiScreenForm.js
imports...
import MultiScreenFormNavigator from './MultiScreenFormNavigator'
export const FormContext = React.createContext()
class MultiScreenForm extends Component {
constructor(props) {
super(props)
this.state = {
// formDataHere
formUpdaters: {
onToggleOptIn: this.handleToggleOptIn // example updater method
// other
}
}
}
handleToggleOptIn = () => {
// toggle opt in form data with this.setState
}
render() {
return (
<FormContext.Provider value={this.state}>
<MultiScreenFormNavigator />
</FormContext.Provider>
)
}
}
export default MultiScreenForm
Example form page
// ProfileForm.js
imports...
import { FormContext } from './MultiScreenForm'
class ProfileForm extends Component {
render() {
// FormContext.Consumer uses function as child pattern
return (
<FormContext.Consumer>
{ (context) => (
// our form can now use anything that we pass through the context
// earlier, we passed the root form's state including an updater
<button onPress={context.formUpdaters.onToggleOptIn} />
// ...
)
}
</FormContext.Consumer>
)
}
}
export default ProfileForm
Tab navigator
// MultiScreenFormNavigator.js
imports...
import ProfileForm from './ProfileForm'
import { createMaterialTopTabNavigator } from 'react-navigation'
const MultiScreenFormNavigator = createMaterialTopTabNavigator(
{
Profile: ProfileForm,
// AnotherForm: AnotherForm
},
// { navigator options here... }
)
export default MultiScreenFormNavigator
We then render the MultiScreenForm instead of the tab navigator directly.
This worked for me but I feel there should be an easier way to do this. I hope people who read this can share their approaches.
#tempomax
tried same with AsyncStorage but data came in with a delay.
Sometimes you don't need Redux if your app stays small.
So tried to find a way without Redux.
Here is what I came up with
I hope it's not too late to answer.
Solved it with NavigationEvents and setting params to Route.
The problem with tab is that you canĀ“t pass params to screen because navigation.navigate will be triggered automatically if createMaterialTopTabNavigator is swiped or clicked on non-active TabBar Button.
This can be solved with NavigationEvent like follow.
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus',payload)}
onDidFocus={payload => console.log('did focus',payload)}
onWillBlur={payload =>
/*
if screen is about to change this will be triggred
In screen 'MyScreen2' you can get it with navigation.params
*/
this.props.navigation.navigate('MyScreen2', { name: 'Brent' })
}
onDidBlur={payload => console.log('did blur',payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;
Now you can get the data in MyScreen2
/* 2. Get the param, provide a fallback value if not available */
const { navigation } = this.props;
const itemId = navigation.getParam('name', 'DefaultName');
const otherParam = navigation.getParam('otherParam', 'some default value');
If you are using React Native Navigation Version 5.x with a DrawerNavigation, you can do this using
in screen 1:
<Button
onPress={() => {
this.props.navigation.navigate(<ScreenNameOfDrawerScreen>,
{screen:'<ScreenNameInTabDrawer>',params:{your_json_Data}});
}} />
in screen 2:
............
render() {
if(this.props.route.params!=undefined){
if(this.props.route.params.your_json_Data!=null){
// Use this.props.route.params.your_json_Data. It is your json data.
}
}
return (
..............

Use a method in another class and navigation

I am coding an application that is neccessary to login users and I am experiencing a problem.
I have in my App component a getUser () method that I would like to call from other components. I also have a Login () method that allows you to navigate to another page. Only the problem is that I can not export the StackNavigator and App at the same time. Would there be a way to export the StackNavigator and my function? Or an other solution that would solve my problem?
class App extends Component<{}> {
//get the user and return it
_getUser() {
return user
}
//authentify the user and go to is profil
_userLogin() {
navigate('Profil');
}
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Button
title="Login"
onPress={() => this.Login() }
/>
</View>
);
};
}
export default StackNavigator({
Home: { screen: App },
Profil: { screen: secondScreen },
});
Thanks for your help.
Finally i found a solution, I ll store the user in an asynchron storage.