Set initialRouteName dynamically in React Navigation TabNavigator - react-native

I want to set initialRouteName of my TabNavigator dynamically in splash screen. In splash, i decide initialRouteName based on an api result but i don't know how to pass this route name to my router file.
Router.tsx:
public static Routes = StackNavigator({
Splash: { screen: Splash },
Verification: { screen: Verification },
Login: { screen: Login },
Main: {
screen: TabNavigator({
Calendar: { screen: Calendar },
PendingReserves: { screen: PendingReserves },
Profile: { screen: Profile },
},
{initialRouteName: 'Calendar'}) // I want to set it from splash
}
}, {initialRouteName: 'Splash'} );
splash.tsx:
constructor(props: SplashScreenProps, context: any) {
super(props, context);
this.state = {
Error: false
}
this.check()
}
check = async () => {
let pending = await dataService.getPendingReserves(data);
if (pending.Success) {
if (pending.result.length > 0) {
// go to pendingReserve Tab
} else {
// go to Calendar Tab
}
} else {
this.setState({ Error: true })
}
}
I tried to route statically to PendingReserves and in it's constructor, check the api and change tab if necessary but i couldn't change tab programmatically with this code:
pendingReserves.tsx:
fetchData = async () => {
let res = await Utilities.getPendingReserves()
if (res && res.length > 0) {
...
} else {
const resetAction = NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Calendar' })]
})
this.props.navigation.dispatch(resetAction)
// this.props.navigation.navigate({ routeName: 'Calendar' })
// both reset and simple navigate are not works correctly...
}
}
So any solution for dynamically set initialRouteName OR change tab programmatically will help me. Thanks
I'm using:
"react-native": "0.49.3",
"react-navigation": "^1.0.0-beta.13"

Finally... I didn't find a way to change tab programmatically in react navigation version 1, so i update to version 2 and this.props.navigation.navigate('Calendar') works correctly.

Related

How to navigate to a screen when opening push notification in React Native?

code
const MypageStack = createStackNavigator(
{
News,
Mypage,
},
{
initialRouteName: 'Mypage',
},
);
const postLoginNavigator = createBottomTabNavigator({
Mypage: MypageStack,
});
const AppNavigator = createStackNavigator({
Loading,
First,
PostLogin: postLoginNavigator,
}, {
mode: 'card',
headerMode: 'none',
initialRouteName: 'Loading',
defaultNavigationOptions: {
gestureEnabled: false,
},
});
const AppContainer = createAppContainer(AppNavigator);
export default class App extends React.Component {
async componentDidMount() {
Notifications.addListener(this.subscribeNotification);
}
subscribeNotification = (notification) => {
const { screen = null } = data;
// screen = 'News'
if (notification.origin === 'selected') {
if (screen) {
dispatch(NavigationActions.navigate({ routeName: screen }));
}
}
}
render() {
return (
<AppContainer />
);
}
}
What I'm trying to do
When opening notification, I want to navigate to News screen.
But, dispatch(NavigationActions.navigate({ routeName: screen })); doesn't work.
I got an error.
can't find variable: dispatch
I don't know how to navigate. I would appreciate it if you could give me any advice.
To dispatch a navigation action you have to use a root navigation object from you props like this =>
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'HOME_SCREEN' })] });
this.props.navigation.dispatch(resetAction);
But I found it more convenient to create a common file called NavigationService.js to manage your navigation actions, like this one
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
function getCurrentRoute() {
let route = _navigator.state.nav
while (route.routes) {
route = route.routes[route.index]
}
return route
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
getCurrentRoute
};
Update your <AppContainer/> to pass the navigation refs to NavigationSerive file
<AppContainer ref={ref => NavigationService.setTopLevelNavigator(ref) }
And then anywhere in your app just import and call whatever you need
import NavigationService from './NavigationService';
NavigationService.navigate(routeName, { ...yourParams })
Happy Coding

didFocus doesn't work while navigating from another screen

I am using "react-navigation 3.11.0" with my react native expo application and I have below structure of navigation.
const orderStackNavigator = createStackNavigator(
{
orders: {
screen: Orders
},
orderdetail: {
screen: OrderDetail
},
ordermoredetails: {
screen: OrderMoreDetails
},
ordernotes: {
screen: OrderNotes
},
orderbillingdetails: {
screen: OrderBillingDetails
},
orderdeliverydetails: {
screen: OrderDeliveryDetails
}
},
{
//headerMode: 'none'
defaultNavigationOptions: {
headerStyle: [styles.headerStyle]
}
}
);
const inventoryManagerStackNavigator = createStackNavigator(
{
categories: {
screen: Categories
},
products: {
screen: Products
},
editProduct: {
screen: EditProduct
}
},
{
//headerMode: 'none'
defaultNavigationOptions: {
headerStyle: [styles.headerStyle]
}
}
);
const tabNavigator = createBottomTabNavigator(
{
orders: {
screen: orderStackNavigator,
},
inventory: {
screen: inventoryManagerStackNavigator,
},
},
{
order: ["orders","inventory"],
animationEnabled: true,
tabBarOptions: {
activeTintColor: "#026AC2",
inactiveTintColor: "#86AAC2",
labelStyle: { fontFamily: "ClearSans-Regular" }
//iconStyle: { fontFamily: 'ClearSans-Bold' }
}
}
);
const mainStackNavigator = createStackNavigator(
{
login: {
screen: Login
},
oAuth: {
screen: OAuth
},
tabs: {
screen: tabNavigator
}
},
{
initialRouteName: "login",
headerMode: "none"
}
);
const AppContainer = createAppContainer(mainStackNavigator);
export default AppContainer;
I am facing issue like if I navigate from ordermoredetails to editProduct it will not add didFocus listener to navigation. If I once navigate to inventory and then editProduct it will work as expected even from ordermoredetails but if user go to ordermoredetails and navigate to editProduct it do not work. Below is my code for editProduct for adding Listener.
componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener("didFocus", () => {
if (this.props.needToReload == true) {
//do the stuff
}
});
}
componentWillUnmount() {
// Remove the event listener
if (this.focusListener != null && this.focusListener.remove)
this.focusListener.remove();
}
Can any one please let me know how can i fix this and make didFocus call every time component loads?
changing "didFocus" to "focus" inside navigation.addListener worked for me.
"didFocus" is not working with me. Please try "focus". Below is the sample code which is working with me. I have version: "#react-navigation/native": "^5.7.6",
componentDidMount() {
this.focusListener = this.props.navigation.addListener('focus', () => {
// your logic will go here
});
}
componentWillUnmount() {
// Remove the event listener
this.focusListener.remove();
}

React Navigation - Jumping between tabs

Hello guys I have 5 Containers in my tab bar and I would like to know if there is an option to return to the main screen if I select another tab container.
So I have the following
https://snack.expo.io/#react-navigation/stacks-in-tabs-v3
While I am in Home tab I click on Go to Details and then I switch to Settings tab. If I return to Home container I want to see the default screen which is HomeScreen
Any option?
You can redefine the tabBarOnPress of the navigationOptions of each StackNavigator like so:
const stacks = [
{ Home: HomeScreen, Details: DetailsScreen },
{ Settings: SettingsScreen, Details: DetailsScreen }
].reduce((stacksObj, stack) => {
const initialRoute = Object.keys(stack)[0];
stacksObj[initialRoute] = createStackNavigator(
stack,
{
navigationOptions: {
tabBarOnPress: ({ navigation }) => navigation.navigate({
routeName: initialRoute,
action: navigation.popToTop()
})
}
}
);
return stacksObj
}, {});
export default createAppContainer(createBottomTabNavigator(
stacks,
{ /* same defaultNavigationOptions as the snack example */ }
));
Cause your DetailScreen is nested in stack Home, you must navigate one more level to that screen
_navigateHome = () => {
const navigateAction = NavigationActions.navigate({
routeName: 'Home',
action: NavigationActions.navigate({ routeName: 'Home' }),
});
this.props.navigation.dispatch(navigateAction);
}
Looks the api has changed , the new way to achieve this is :
import { CommonActions } from '#react-navigation/native';
navigation.dispatch(
CommonActions.navigate({
name: 'Profile',
params: {
user: 'jane',
},
})
);

Not able to store logged In user data, using AsyncStorage API

I am new to react native so please bear with me.
Here is my access token's async storage, the token is one of the parameters as the response from API. and this code is in Login Screen
async getToken() {
try{
const token = await AsyncStorage.getItem('ACCESS_TOKEN');
if(token !== null) {
this.props.navigation.navigate('Profile')
}
else{
this.props.navigation.navigate('Login')
}
}
catch (error){
console.log('Something went wrong');
}
}
async storeToken(accessToken) {
try{
await AsyncStorage.setItem('ACCESS_TOKEN', accessToken);
this.getToken();
}
catch (error) {
console.log('something went wrong');
}
}
when I log-in, it is navigating me to Profile page but after refreshing the app it is going back to the login page or my navigation tree
Here is my app.js where navigation is set
const TodoNav = StackNavigator({
Login: { screen: Login },
SignUp: { screen: SignUp },
ForgotPassword: { screen: ForgotPassword },
Profile: { screen: Profile },
MenuScreen: { screen: MenuScreen },
}, {
mode: 'modal'
})
export default class App extends Component{
render() {
return (
<TodoNav />
);
}
}
AsyncStorage is not responsible to save the navigation state of your application, it will store the data as you pass to it. Refer the documentation of AsyncStorage for more details(Here)
In order to show Home screen, you need to check your async storage data at App.js
Then create Navigation stack accordingly, for eg:
If you find AccessToken:
const TodoNavAuthenticated = StackNavigator({
Profile: { screen: Profile },
MenuScreen: { screen: MenuScreen },
}, {
mode: 'modal'
})
If Accesstoken is not found means you are not logged in so:
const TodoNav = StackNavigator({
Login: { screen: Login },
SignUp: { screen: SignUp },
ForgotPassword: { screen: ForgotPassword },
Profile: { screen: Profile },
MenuScreen: { screen: MenuScreen },
}, {
mode: 'modal'
})
This needs to be checked when your using <ToDoNav /> i.e. inside your App.js render() method.

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