Disable loading of initialRoute inside stack navigator when - react-native

I have two stacks in my react-native app similar to this structure. When I navigate a user from Home screen inside MainStack to Quiz screen inside QuizStack, the componentDidMount method of QuizList screen is being called. How do I prevent this? I want only the componentDidMount of Quiz screen to be called.
export const MainStack = createStackNavigator({
Home: { screen: Home },
Game: { screen: Game },
Leaderboard: { screen: Leaderboard }
}, { initialRouteName: 'Home' })
export const QuizStack = createStackNavigator({
QuizList: { screen: QuizList },
Quiz: { screen: Quiz },
QuizResult: { screen: QuizResult }
}, { initialRouteName: 'QuizList' })
export const InternalStacks = createBottomTabNavigator({
QuizStack: { screen: QuizStack, navigationOptions: { tabBarLabel: 'Quiz' } },
MainStack: { screen: MainStack, navigationOptions: { tabBarLabel: 'Home' } },
})
What makers matters worse is, on sending a push notification, users are directed automatically from Home(MainStack) to Quiz (QuizStack). The componentDidMount method in Quiz calls an API, so if 10K users opens notifications, that 10K APIs called. Then unecesarily the componentDidMount method of QuizList(QuizStack) is also being called, which is an additional 10K APIs

It is unclear to me why you have two separate stacks, and how they are connected. Maybe by a top level navigator? You should try to merge the stacks into a single one.
About the Push notification, you can create a SwitchNavigator:
const SwitchNavigator = createSwitchNavigator(
{
Loading: LoadingScreen,
Root: RootStack
},
{
initialRouteName: 'Loading'
}
)
And in the componentDidMount in LoadingScreen, you will check the initial URL received by the push.
async componentDidMount() {
const { navigation } = this.props
const initialURL = await Linking.getInitialURL()
if (initialURL && initialURL.startsWith('myapp://myapp/xxx')) {
navigation.navigate('MyScreenA', { options })
} else {
navigation.navigate('MyScreenDefaultScreen')
}
}

Related

Prevent for user to access "Auth Loading Screen" on android

In my app (and most likely like any other app) I have a loading screen which checks if the user is either logged in or not. Thing is that the user on android right now has the possibility of returning to that screen if the "back" button is pressed. How is it possible for me to avoid for the user doing that?
This is what I have on my code:
const loadingStack = createSwitchNavigator({
Loading: {
screen: Loading
}
}, {
defaultNavigationOptions: {
unmountOnBlur: true,
headerMode: 'none',
gestureEnabled: false
}
}
);
export default createAppContainer(
createStackNavigator({
Loading: loadingStack,
App: {
screen: AppTabNavigator,
navigationOptions: {
gestureEnabled: false,
},
},
Auth: {
screen: AuthStack,
},
...
I see two solutions:
Change your navigation and opt for a switch navigation instead.
createAppContainer(
createSwitchNavigator(
{
Loading: {
screen: Loading,
navigationOptions: {
headerMode: 'none',
}
},
App: {
screen: AppTabNavigator,
navigationOptions: {
gestureEnabled: false
}
},
Auth: {
screen: AuthStack
}
}
)
)
If you want to "enjoy" the animations of the StackNavigator, when you navigate from your Loading to your App, you replace your current view with your new view.
//Example on your Loading Screen
this.props.navigation.replace({ routeName: "App" })
This will replace your Loading Screen view with your App "View", when you go back, you will not be redirected to the Loading Screen anymore because it is no longer in the stack, it has been replaced by App.
EDIT (In response to the comment because it's too long):
In this case, if you want to make a special backButton management on the Home you can use the React Native BackHandler.
For example, if you want nothing to happen when you click on the backButton, add an eventListener that returns true no matter what:
import { BackHandler } from 'react-native'
...
componentDidMount() {
this.backHandler = BackHandler.addEventListener('hardwareBackPress', () => true)
}
componentWillUnmount(){
this.backHandler.remove()
}
Otherwise, if you want the app to quit, for example:
this.backHandler = BackHandler.addEventListener('hardwareBackPress', () => BackHandler.exitApp())

Screen navigate from stacknavigator is not working when using bottomtabnavigator

I'm new to react native, and I've problem with navigating between one of my screen to another.
I'm using stacknavigator like this:
const RootStack = createStackNavigator(
{
Splash: { screen: SplashScreen},
Mobile:{screen:MobileAuthentication} ,
Code:{screen:CodeAuthentication} ,
Home: { screen: HomeScreen },
ProfileScreen: { screen: ProfileScreen },
ProfileDetails: { screen: ProfileDetails },
},
{ initialRouteName: 'Splash'}
);
const AppContainer = createAppContainer(RootStack);
In my homeScreen I am using buttomtabnavigator
const bottomTabNavigator = createBottomTabNavigator(
{
A: {
screen: ProfileScreen,
navigationOptions: {
...
}
},
B: {
screen: FilterScreen,
navigationOptions: {
...
}
},
},
{
initialRouteName: 'FilterScreen',
tabBarOptions: {
activeTintColor: '#3F51B5'
}
}
);
const AppContainer = createAppContainer(bottomTabNavigator);
The problem is that when I want to navigate from ProfileScreen to ProfileDetails by onpress then it's not working
<ProfileBtn
onPress={() => {
console.log('item Clicked'),
this.props.navigation.navigate('ProfileDetails')
} }
/>
Maybe you should try to pass "navigation" into your stackNavigator creation, using the options with smthg like this (for each of your stack screen or at least those within which you want to use it) :
options={({route, navigation}) => (
{headerTitle: 'Splash',
route: {route},
navigation: {navigation}}
)}
If that's not the answer, please provide us with the error you get in your console.

react-navigation: is this really the only way to access sibling and parent navigators?

So, I have a somewhat complicated nesting of navigators in my app (4 layers), and it is useful to be able to grab data from the params of each.
For instance, I have tabs, within tabs, that are inside a stack navigator. To access params from one of the screens therein I have do this:
const homeSiblingNavigator = props.navigation.dangerouslyGetParent().dangerouslyGetParent().getChildNavigation('Home');
First of all, calling dangerouslyGetParent() inspires absolutely no confidence.
Secondly, I feel like there should be a way, if you uniquely name every route, to find route by name.
I'm thinking of writing some weird recursive/loopy route finder. But before I do, I'm curious if there is a better way.
You can specify the path of the screen. And to get the parameters of the screen, getActionForPathAndParams can be used.
routing example
const AuthSwitch = createAppContainer(createStackNavigator({
AuthLoading: { screen: AuthLoadingScreen },
App: {
screen: AppStack,
path: '',
},
Auth: { screen: AuthStack },
}));
const AppStack = createStackNavigator({
Home: { screen: HomeScreen },
Friends: {
screen: FriendsScreen,
path: 'friends',
},
});
const FriendsScreen = createStackNavigator({
Overview: { screen: OverviewScreen },
Chat: {
screen: ChatScreen,
path: 'chat/:user',
},
});
getActionForPathAndParams Example
import { NavigationActions } from 'react-navigation'
const MyApp = createStackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen },
}, {
initialRouteName: 'Home',
})
const previousGetActionForPathAndParams = MyApp.router.getActionForPathAndParams;
Object.assign(MyApp.router, {
getActionForPathAndParams(path, params) {
if (
path === 'my/custom/path' &&
params.magic === 'yes'
) {
// returns a profile navigate action for /my/custom/path?magic=yes
return NavigationActions.navigate({
routeName: 'Profile',
action: NavigationActions.navigate({
// This child action will get passed to the child router
// ProfileScreen.router.getStateForAction to get the child
// navigation state.
routeName: 'Friends',
}),
});
}
return previousGetActionForPathAndParams(path, params);
},
});

How do I nest a drawer navigator inside a page that exists inside a Stack?

I want to create a small react native app with just a few screens: Login and Home.
However, I would also like to have a drawer navigator attached to the Home screen. The drawer will contain 3 additional screens: Device Registration, Call, and Sound Check. So far my App.js file looks like this
import { StackNavigator, DrawerNavigator } from 'react-navigation';
import Login from './src/components/Login';
import SignUp from './src/components/SignUp';
import DeviceRegistration from './src/components/DeviceRegistration';
import Call from './src/components/Call';
import Sound from './src/components/Sound';
export const Navigation = StackNavigator({
Login: { screen: Login },
SignUp: { screen: SignUp },
DeviceRegistration: { screen: DeviceRegistration },
Call: { screen: Call },
Sound: { screen: Sound },
},
{
headerMode: 'none',
initialRouteName: 'Login'
}
);
export const Drawer = DrawerNavigator({
DeviceRegistration: { screen: DeviceRegistration },
Call: { screen: Call },
Sound: { screen: Sound },
}, {
});
export default Navigation;
Where do I put the Drawer inside of the Home screen so that I can create a hamburger icon which I can press to access the drawer navigation?
export const Drawer = DrawerNavigator({
SignUp: { screen: SignUp },
DeviceRegistration: { screen: DeviceRegistration },
Call: { screen: Call },
Sound: { screen: Sound },
});
export const Navigation = StackNavigator({
Login: { screen: Login },
Home: { screen: Drawer },
});
Then in your Signup component, call this.props.navigation.navigate('DrawerOpen') to open the drawer.

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);
}};