React navigation how to navigate to children through parent? - react-native

So I have this react navigation setup:
Stack navigator
|Tab navigator
| | Screen_A
| | Screen_B
| Screen_C
| Screen_D
Normal case, user would go to screen A, then click to go to screen C with some params. From screen B, user click to go to screen D.
Now on screen D, when user click, I need him to go to screen C but through screen A (because when user click back from C, they need to go back to screen A).
I tried to pass params to screen A from screen D, and in A's render() method, check for params and continue navigate to screen C, and it works. But I got warning "Cannot update during an existing state transition(such as within 'render'...". So how can I accomplish this without triggering any warning?
Thanks

A possible alternative solution would be to leverage the customized stack router, here I have came up with a possible solution.
In this case, when you navigate from ScreenD to ScreenC, you can directly navigate to ScreenC, but then when the user on ScreenC and try to go back, we will insert ScreenA into the stack so the user will see ScreenA instead of ScreenD.
const NestedNavigator = TabNavigator({
ScreenA: { screen: ScreenA },
ScreenB: { screen: ScreenB },
});
const MainNavigator = StackNavigator({
Home: {
screen: NestedNavigator
},
ScreenC: { screen: ScreenC },
ScreenD: { screen: ScreenD },
});
const defaultGetStateForAction = MainNavigator.router.getStateForAction;
MainNavigator.router.getStateForAction = (action, state) => {
if (state && action.type === 'Navigation/BACK' && state.routes[state.index].routeName === 'ScreenC') {
const routes = [
...state.routes,
{index: 0, key: 'ScreenA', routeName: 'Home', routes: [{ key: 'ScreenA', routeName: 'ScreenA'}, { key: 'ScreenB', routeName: 'ScreenB' }]},
];
return {
...state,
routes,
index: routes.length - 1,
};
}
return defaultGetStateForAction(action, state);
};

Related

Authentication Flow in React Native

I'm having issue creating the authentication flow of my app. What I actually want is to navigate user conditionally based on their role.
By default, I have created an AuthStack which is basically a stackNavigator and it has a login page. Once user logs in, we receive user's role through a network request. Next I navigate him to a simple home page that returns nothing but switchNavigator based on his role. Here's the code for better clarity.
const AuthStack = createStackNavigator({
Login: {
screen: Login
},
SignUp: {
screen: SignUp
},
Home: {
screen: Home
}
},
{
initialRouteName: 'Login',
headerMode: 'none'
});
const AppContainer = createAppContainer(AuthStack);
const Navigation = () => {
return <AppContainer />
}
When user logs in, I redirect him to Home screen shown in above mentioned stack. Here's the code in Home screen:
const Home = (props) => {
const AppContainer = createAppContainer(RootNavigator(props.user.role))
return <AppContainer />
}
Here I create a new app container (which might be the bad practice, advice please). And RootNavigator is a helper function which returns a switchNavigator:
export const RootNavigator = (user) => {
return createSwitchNavigator({
Admin: {
screen: AdminDrawerNavigator
},
Reporter: {
screen: ReporterDrawerNavigator
}
},
{
initialRouteName: user === 'admin'? 'Admin': 'Reporter'
})
}
This all works fine but it seems switchNavigator doesn't seem to work correctly. If I press back button of hardware, it goes back to login page. Thank you for your time. Please suggest the possible solution.
Try to reset you history navigation :
NavigationActions.reset({
index: 1,
actions: [NavigationActions.navigate({
routeName: 'Admin'
}),
]
});

Back button handler working on every screen after changing drawer navigator in react - native

I am making a react native application in which there can be two type of user model and member.Now each having different drawer navigator as both having different data and both these drawer are inside a main stack navigator which is main navigator for the application. Now in both the both the drawer navigator .
const RootStack = createStackNavigator({
Splash : { screen: Splash },
PrivacyPolicy : { screen: PrivacyPolicy},
Welcome : { screen: Welcome },
Login : { screen: Login },
HomeMember: {screen: memberdrawerNavigator},
HomeModel: {screen: modeldrawerNavigator},
OtpChangePassword : { screen: OtpChangePassword},
}, {
headerMode: 'none',
initialRouteName: 'Splash'
})
I have a homepage different for both the member type and in both the home page i have implemented the back handler like this.
handleBackWithAlert = () => {
if (this.props.isFocused) {
if(this.state.loading_status){
this.setState({loading_status:false})
}
else{
Alert.alert(
'Exit App',
'Exiting the application?',
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel'
},
{
text: 'OK',
onPress: () => BackHandler.exitApp()
}
],
{
cancelable: false
}
);
}
return true;
}
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress',this.handleBackWithAlert);
//this.props.navigation.dispatch(DrawerActions.closeDrawer());
this.props.navigation.closeDrawer();
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackWithAlert);
}
Since i wanted to check back handling on only specific page so i exported my component like this.
export default withNavigationFocus(HomePage);
Now what the problem is that suppose i am using member account and after that i logout and then login with model account then this back handler is working on every screen why ? And if i close or kill that app without logout and restart then this problem disappears.
Here is my code called on logout
AsyncStorage.clear()
ToastAndroid.show("Logged Out Successfully !",ToastAndroid.SHORT);
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Welcome' })],
});
this.props.navigation.dispatch(resetAction);
Kindly help me to solve this problem. Thanks in advance.
According to my understanding, something is wrong with your BackHandler listeners. maybe you're not removing BackHandler listener on componentWillUnmount?

React navigation unable to navigate to just pervious screen after navigating multiple screens

Scenario:
Navigate: Screen 'StoreCategories' --> Screen 'QRScanner'
Back press while at Screen 'QRScanner' redirect to Screen 'StoreCategories' working perfectly
Navigate: Screen 'QRScanner' --> Screen 'Options' and
Navigate: Screen 'Options' --> Screen 'QRScanner' again
Click on back while at Screen 'QRScanner' redirect to Screen 'StoreCategories' again
Not redirecting to its previous Screen 'Options'.
Everything happens with default back functioning in react-navigation, I am not using any custom function for back functioning.
How can I resolve it and navigate to just previous from redirection happens rather than already cached screen in react-navigation(V2)?
const StackNavigator = defaultRoute =>
createStackNavigator(
{
CustomerLogin: {
screen: CustomerLogins
},
MerchantLogin: {
screen: MerchantLogin
},
Profile: {
screen: Profile
},
OtpVerification: {
screen: OtpVerification
},
StoreOffers: {
screen: StoreOffers
},
StoreCategories: {
screen: StoreCategories
},
QRScanner: {
screen: QRScanner
},
MerchantHome: {
screen: MerchantHome
},
CustomerHome: {
screen: CustomerHome
},
ThankYou: {
screen: ThankYou
},
Detail: {
screen: Detail
},
Options: {
screen: Options
},
PayOnline: {
screen: PayOnline
},
PayCash: {
screen: PayCash
},
PayTm: {
screen: PayTm
},
Token: {
screen: Token
},
AddDiscount: {
screen: AddDiscount
},
CustomerHistory: {
screen: CustomerHistory
},
Webview: {
screen: Webview
},
Direction: {
screen: Direction
}
},
{
initialRouteName: defaultRoute
}
);
The way the current React-Navigation's navigate function works is a bit different then how they work previously. Currently if you navigate to a screen that is already in the screen stack, the system would go back to that screen (removing the other screens between the current screen and that screen will be remove) instead of simply adding a new screen to the stack.
If you want to use the old system style, you can use push instead of navigate. This would simply add to the stack instead of going back to the last one (if exist in the stack).
So rather than use
const {navigate} = navigation;
navigate(nextScreen, params);
you use
const {push} = navigation;
push(nextScreen, params);
if You want to navigate to back screen, you can use this code:
this.props.navigation.goBack()
by navigate to Main it will redirect to initial route of Main and it seems to be A

navigating user on notification click app in background/foreground

I'm using REACT-NATIVE-FCM.
My app has drawer navigator inside stack navigator in App.js.
On notification click from notification tray I want user to directly go to certain Screen.
Registering FCM events in Home page only work if app is closed, while in app in foreground the events are not called.It just takes you to the last open Screen.
I have to register FCM events on every Screen, which doesn’t look ok to me. So I have the solution to register FCM events in App.js since App.js is called from index.js while initializing the App but I’m unable to use this.props.navigation.navigate in App.js.
Using react navigation how can we redirect user just after stack is declared.
APP.JS
const Drawer = DrawerNavigator(
{
Dashboard: { screen: Dashboard },
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2},
Screen3: { screen: Screen3},
},
{
navigationOptions: {
gesturesEnabled: false,
},
initialRouteName: "Dashboard",
contentOptions: {
activeTintColor: "#e91e63"
},
drawerPosition: 'right',
contentComponent: props => <SideBar {...props} />
}
);
const AppNavigator = StackNavigator(
{
Home: { screen: Home },
Drawer: { screen: Drawer },
ScreenABC: { screen: ScreenABC },
ScreenXYZ: { screen: ScreenXYZ }
},
{
headerMode: "none",
}
);
export default () =>
<Root>
<AppNavigator />
</Root>;
//CALLED WHEN APP IN FOREGROUND
this.notificationListener = FCM.on(FCMEvent.Notification, async (notif) => {
//alert('FCM.on: ' + JSON.stringify(notif));
if(notif.routeValue == 1)
{
this.props.navigation.navigate("Screen1")}
});
//CALLED WHEN APP IN BACKGROUND
FCM.getInitialNotification().then(notif => {
});
If you want to really go to a certain screen no matter what. You should at least reset your navigator state before you call navigate.
Or you can also do is creating a custom action, dispatch this action and return a correct navigator state.
Here is how I reset my navigator state to home:
export const homeNav = (state, action) => {
let index;
let routes;
switch (action.type) {
case types.RESET_HOME_NAV:
routes = [...state.routes];
for (let index = state.index; index > 0; --index) {
routes.pop();
}
return {
...state,
routes,
index: 0,
};
default:
return NavigatorHome.router.getStateForAction(action, state);
}};

How to jump to different stack navigator and close previous one with react-navigation?

I am using react-navigation and have 2 distinct stack routers in separate files AuthedRouter and NonAuthedRouter. They each have unrelated screens with no overlap.
AuthedRouter looks like:
const AuthedRouter = StackNavigator({
Home : { screen: Home }
})
NonAuthedRouter.js looks like:
const NonAuthedRouter = StackNavigator({
Signup: { screen : Signup }
SignupTwo : { screen : SignupTwo }
})
In my App.js I check the user app status and in my render method return NonAuthedRouter if user needs to signup. My problem is how do I then navigate to the Home screen in the AuthedRouter once signup is done? Also I want to make sure its dont properly and that the NonAuthedRouter screens are 100% removed from memory/etc.
Thanks.
Instead of having two StackNavigator, u can perform the same using just one.
const MainRouter = StackNavigator({
Signup: { screen : Signup },
SignupTwo : { screen : SignupTwo },
Home : { screen: Home }
})
If the user already signed up, just navigate to Home Screen and the user cannot go back from Home screen because there is no screen in the stack.
If its a new user then navigate to Signup screen, then to SignupTwo screen and ones sign up is completely done just reset the stack and navigate to Home screen. Reset will clear the stack screen. Read react-navigation docs for further information
The way to reset a StackNavigator
this.props.navigation.dispatch(NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Home'})
]
}));
This is how I am Navigating to the "Drawer Stack" after I reset the previous "Login Stack, when I am navigating:"
export const Drawer = createDrawerNavigator(
{
MyAccount: {screen: TabsStack},
},
export const LoginStack = createStackNavigator(
{
Login: {screen: LoginPage},
}
);
export const RootStack = createStackNavigator({
Login: {screen: LoginStack},
Drawer: {screen: Drawer},
},
{
initialRouteName: 'Login',
headerMode: 'none'
},
then on "Login Screen" you do:
on a Button Press/Click:
<TouchableOpacity style={styles.signuptext} onPress={this.onLoginSuccess.bind(this)}>
<Text style={styles.signuptext}> Sign Up Now </Text>
</TouchableOpacity>
you call the "onLoginSuccess" Method like this:
onLoginSuccess = () =>{
{
const navigateAction = StackActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: 'Drawer' })]
})
this.props.navigation.dispatch(navigateAction)
}
Note: This basically resets the "Login" stack while navigating you to the "Drawer" Stack. So now when you will press the back button, you cannot return to the Login Screen of the "Login Stack".