Navigate between two stack Navigators after login in react native - react-native

This is my first stack
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="Home" component={MainScreen} />
<Stack.Screen name="Schools" component={SchoolsScreen} />
<Stack.Screen name="Setting" component={SettingScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Saved" component={SavedData} />
<Stack.Screen name="Profile2" component={Profile2} />
<Stack.Screen name="SchoolDetails" component={SchoolDetailsScreen} />
<Stack.Screen name="Bottom" component={BottomTab} />
</Stack.Navigator>
</NavigationContainer>
This is my second stack
<NavigationContainer>
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="Login" component={AuthLogin} />
<Stack.Screen name="Register" component={AuthRegister} />
<Stack.Screen name="Forget" component={ForgetPassword} />
</Stack.Navigator>
</NavigationContainer>
THis is my app.js file
const [auth, setAuth] = useState(false);
useEffect(() => {
(async () => {
const value = await AsyncStorage.getItem("isLoggedin");
console.log(value);
setAuth(value);
})();
}, []);
return (
<SafeAreaView style={{ flex: 1 }}>
<NativeBaseProvider config={config}>
{auth == "true" ? <InsideStack /> : <OutsideStack />}
</NativeBaseProvider>
</SafeAreaView>
);
}
I want to navigate to home screen from login screen , after function call
// navigation.push("Home");
const storeData = async (value) => {
try {
await AsyncStorage.setItem("isLoggedin", JSON.stringify(true));
} catch (e) {
// saving error
}
};
storeData();
navigation.push("Home");
}}
But i got error:
Do you have a screen named 'Home'?

this is the simple code for this. you have to use redux or context api for gloal state management. here is a min example ;
import {NavigationContainer} from '#react-navigation/native';
import React from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {GetUserContacts} from '../logic/getcontact';
import {setContacts} from '../store/actions/ContactActions';
import AuthNavigation from './AuthNavigation';
import RootNavigation from './RootNavigation';
const AppContainer = () => {
const {is_logged_in} = useSelector(state => state.persistedReducer);
const dispatch = useDispatch();
if (is_logged_in) {
GetUserContacts('PK').then(data => {
dispatch(setContacts(data));
});
}
return (
<NavigationContainer>
{is_logged_in ? <RootNavigation /> : <AuthNavigation />}
</NavigationContainer>
);
};
export default AppContainer;
after user logged in you have to make that is_logged_in true. so the navigator changes itself. you will have to persist this so when application restart the user dont need to login again

Related

Mixing both Stack navigation and tab navigation in react native

I'm trying to mix stack navigation and tab navigation i have 14 Screens:
Search,
Home,
CartTab,
SideBar,
Details,
PriceComparison,
ShopByCategory,
ShopByStore,
CategoryPage,
CategoryFilter,
Checkout,
Login,
Register,
ForgotPassword
I want to have Search, Home, CartTab, Sidebar as bottom navigation and the other pages as stack navigation, with SearchBy pages, Details, Price Comparison not displaying the bottom tab. I'm new to react native and i have been trying diffrent solutions however 1) i'm not able to remove the bottomtab from the wanted screens and 2) using navigation.navigate is returning me errores example using navigation.navigate('Category'); i get an error The action 'NAVIGATE' with payload {"name":"Category"} was not handled by any navigator. Do you have a screen named 'Category'?
This is my code :
StackNavigator.js:
import React from 'react'
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import {
CartTab,
Details,
Home,
Notification,
PriceComparison,
Search,
SplashScreen,
ShopByCategory,
ShopByStore,
CategoryPage,
Checkout,
CategoryFilter,
Login,
Register,
ForgotPassword
} from '../screens';
import { SideBar } from './../components';
export const MainStackNavigator = () => {
const Stack = createStackNavigator();
return (
<Stack.Navigator screenOptions={{headerShown: false}} initialRouteName={'Home'}>
{/*<Stack.Screen name="SplashScreen" component={SplashScreen} /> */}
<Stack.Screen name="Login" component={Login} options={{ tabBarVisible: false }} />
<Stack.Screen name="Register" component={Register} options={{ tabBarVisible: false }} />
<Stack.Screen name="Recovery" component={ForgotPassword} options={{ tabBarVisible: false }} />
<Stack.Screen name="Home" component={Home} options={{ tabBarVisible: false }} />
<Stack.Screen name="Notification" component={Notification} />
<Stack.Screen name="Comparison" component={PriceComparison} options={{ tabBarVisible: false }} />
<Stack.Screen name="ShopByCategory" component={ShopByCategory}/>
<Stack.Screen name="ShopByStore" component={ShopByStore}/>
{/*<Stack.Screen name="Category" component={CategoryPage}/>
<Stack.Screen name="CategoryFilter" component={CategoryFilter}/>*/}
</Stack.Navigator>
);
}
export const CartStackNavigator = () => {
const Stack = createStackNavigator();
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Cart" component={CartTab} />
<Stack.Screen name="Checkout" component={Checkout}/>
</Stack.Navigator>
);
}
export const SearchStackNavigator = () => {
const Stack = createStackNavigator();
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Search" component={Search} />
<Stack.Screen name="Details" component={Details}/>
</Stack.Navigator>
);
}
export const SidebarStackNavigator = () => {
const Stack = createStackNavigator();
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Sidebar" component={SideBar} />
<Stack.Screen name="ShopByCategory" component={ShopByCategory}/>
<Stack.Screen name="ShopByStore" component={ShopByStore}/>
<Stack.Screen name="Category" component={CategoryPage}/>
</Stack.Navigator>
);
}
TabNavigation.js:
import 'react-native-gesture-handler';
import React from 'react';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import Ionicons from 'react-native-vector-icons/Ionicons';
import {COLORS} from '../constants';
import { CartStackNavigator, MainStackNavigator, SearchStackNavigator, SidebarStackNavigator }
from '../navigation/StackNavigator';
import { useStateContext } from '../context/StateContext';
const Tab = createBottomTabNavigator();
export const TabNavigation = ({navigation}) => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false,
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'home'
: 'home-outline';
} else if (route.name === 'Cart') {
iconName = focused ? 'cart' : 'cart-outline';
} else if (route.name === 'Search') {
iconName = focused ? 'search' : 'search-outline';
} else if (route.name === 'SideBar') {
iconName = focused ? 'grid' : 'grid-outline';
}
// You can return any component that you like here!
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: COLORS.primary,
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={MainStackNavigator} />
<Tab.Screen name="Search" component={SearchStackNavigator} />
<Tab.Screen name="Cart" component={CartStackNavigator} options={{ tabBarBadge: 3, tabBarBadgeStyle: { backgroundColor: COLORS.primary, marginTop: -5 } }}/>
<Tab.Screen name="SideBar" component={SidebarStackNavigator} />
</Tab.Navigator>
);
};
App.js:
import React from "react";
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import CustomDrawer from './navigation/CustomDrawer'
import {StateContext} from './context/StateContext'
import { Home, CartTab, Search, SideBar, SplashScreen, Notification, Login, Register,
ForgotPassword } from "./screens";
import { Header, TabNavigation } from "./components";
import { StripeProvider } from '#stripe/stripe-react-native';
const Stack = createStackNavigator();
const App = () => {
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 3000);
}, []);
if (isLoading) {
return <SplashScreen />;
}
return (
<StateContext>
<NavigationContainer>
<TabNavigation/>
</NavigationContainer>
</StateContext>
);
}
export default App
Make two different Stacks file one is StackNavigation and other is BottomNavigation . Render your Stack Navigation and with calling the component of TabNavigation
I used the Dummy names of screens . Replace it according to your screen names
Stack navigation
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import {
CartTab,
Details,
Home,
} from '../screens';
import {TabNavigation } from '../navigation/StackNavigator';
export const StackNavigator = () => {
const Stack = createStackNavigator();
return (
<Stack.Navigator screenOptions={{headerShown: false}}
initialRouteName={'Home'}>
<Stack.Screen name="Home" component={TabNavigation}/>
<Stack.Screen name="ShopByCategory" component={ShopByCategory}/>
<Stack.Screen name="ShopByStore" component={ShopByStore}/>
<Stack.Screen name="CategoryFilter" component={CategoryFilter}/>
</Stack.Navigator>
);
}
Bottom Tab Navigation
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
export const TabNavigation = ({navigation}) => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false}
})}>
<Tab.Screen name="Example" component={Example} />
<Tab.Screen name="Search" component={Search} />
<Tab.Screen name="SideBar" component={SideBar} />
</Tab.Navigator>
);
};

Combine Drawer.Navigator and Stack.Navigator without creating separate navigation bars

I would like to have stack and drawer navigators use the same navigation bar. However, both are creating their own navigation bars refer to the below image making the app look messy. How can I achieve that?
Below is my code:
import * as React from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { createDrawerNavigator } from "#react-navigation/drawer";
import Home from "../screens/Home";
import Contact from "../screens/Contact";
import About from "../screens/About";
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const HomeNavigation = (props) => {
return (
<Stack.Navigator initialRouteName='Home'
screenOptions={{
headerTitleAlign:'center',
//headerShown: false
}}
>
<Stack.Screen name='Home' component={Home} />
<Stack.Screen name='Contact' component={Contact} />
<Stack.Screen name='About' component={About} />
</Stack.Navigator>
);
};
const MenuNavigation = (props) => {
return (
<Stack.Navigator initialRouteName='Home'
screenOptions={{
headerTitleAlign:'center',
//headerShown: false
}}>
<Stack.Screen name='Menu' component={About} />
</Stack.Navigator>
);
};
const DrawerNavigation = () => {
return (
<Drawer.Navigator>
<Drawer.Screen name='HomeNavigation' component={HomeNavigation} />
<Drawer.Screen name='MenuNavigation' component={MenuNavigation} />
</Drawer.Navigator>
);
};
export default DrawerNavigation;

React native - Stack Screen flikering on authentication flow

React native Login Stack Blinks while opening the app, before showing the home page. Using react native navigation V5.
Adding the Token from server to async Storage, then on opening the app, i check for the token in Async storage, if it's not null, i'll navigate to my AppScreens else stay in AuthScreens
AppNavigator
import React, {useState, useEffect} from 'react';
import {connect} from 'react-redux';
import {createStackNavigator} from '#react-navigation/stack';
import AsyncStorage from '#react-native-community/async-storage';
import AddUserData from '../lib/UserDetailsHelper';
//Import screens
import Login from '../Login';
import AddReferalCode from '../AddReferalCode';
import AddUserDetails from '../AddUserDetails';
import DrawerNavigator from './DrawerNavigator';
import BuyCoins from '../BuyCoins';
import PriceList from '../PriceList';
//Quick Play
import QuickPlayGameList from '../QuickPlay/GameList';
import QuickplayRoom from '../QuickPlay/RoomCreationScreen';
import QuickPlayWaitingRoom from '../QuickPlay/waitingRoom';
import QuickPlayQuestions from '../QuickPlay/Questions';
import QuickPlayResult from '../QuickPlay/Result';
import RNBootSplash from 'react-native-bootsplash';
const Stack = createStackNavigator();
const AuthStack = createStackNavigator();
const AppStack = createStackNavigator();
const AuthScreens = (props) => (
<AuthStack.Navigator
screenOptions={{
headerShown: false,
headerTransparent: true,
headerTitle: false,
}}>
{/* Common */}
<Stack.Screen {...props} name="Login" component={Login} />
<Stack.Screen name="AddReferalCode" component={AddReferalCode} />
<Stack.Screen name="Add User Details" component={AddUserDetails} />
</AuthStack.Navigator>
);
const AppScreens = () => (
<AppStack.Navigator
screenOptions={{
headerShown: false,
headerTransparent: true,
headerTitle: false,
}}>
<Stack.Screen name="Drawer" component={DrawerNavigator} />
<Stack.Screen name="BuyCoins" component={BuyCoins} />
<Stack.Screen name="PriceList" component={PriceList} />
{/* <Stack.Screen name="Home" component={Home} /> */}
{/* QuickPlay */}
<Stack.Screen name="QuickPlayGameList" component={QuickPlayGameList} />
<Stack.Screen name="QuickplayRoom" component={QuickplayRoom} />
<Stack.Screen
name="QuickPlayWaitingRoom"
component={QuickPlayWaitingRoom}
/>
<Stack.Screen name="QPQuestions" component={QuickPlayQuestions} />
<Stack.Screen name="QPResult" component={QuickPlayResult} />
</AppStack.Navigator>
);
Routes = (props) => {
const [token, setToken] = useState(null);
console.log('Props acc token:' + props.accessToken);
useEffect(() => {
getToken()
.then((token) => {
setToken(token);
})
.finally(async () => {
await RNBootSplash.hide({fade: true});
});
}, []);
const getToken = async () => {
try {
const token = await AsyncStorage.getItem('AccessToken');
const rating = await AsyncStorage.getItem('UserRating');
console.log('first token:' + token);
setToken(token);
AddUserData.addAccessToken(token);
AddUserData.addUserRating(rating);
return token;
} catch (e) {
console.log(e);
}
};
return token ? <AppScreens /> : <AuthScreens />;
};
const mapStateToProps = (state) => {
return {
accessToken: state.userDetails.accessToken,
};
};
export default connect(mapStateToProps, null)(Routes);
The AuthScreen Stack blinking when opening the app.
ScreenRecoding of the issue
https://drive.google.com/file/d/12p2NtxUbfS-eFt5iGM0PAvhuJZJhUyJQ/view?usp=sharing
Please try placing your home screen BEFORE the login screen and see the effet
<AuthStack.Navigator
screenOptions={{
headerShown: false,
headerTransparent: true,
headerTitle: false,
}}>
// >>> PUT HOME SCREEN HERE
{/* Common */}
<Stack.Screen {...props} name="Login" component={Login} />
<Stack.Screen name="AddReferalCode" component={AddReferalCode} />
<Stack.Screen name="Add User Details" component={AddUserDetails} />
</AuthStack.Navigator>
Additional information: (thanks for Thorin's discovery)
Try to add a Splash screen to the project, and add a small time out of say 10ms, it may be able to fix the problem.

Is is possible to use navigation.toggleDrawer() in navigation options

In my navigation file , when I want to toggle drawer , get the following error :
TypeError: navigation.openDrawer is not a function.(In
'navigation.openDrawer()', 'navigation.openDrawer' is undefined)
This is my drawer:
const DrawerNavigator = () => {
return (
<Drawer.Navigator
initialRouteName="MYSHIFT"
>
<Drawer.Screen name="MYSHIFT" component={TopTabNavigator} />
</Drawer.Navigator>
)
}
And this is my container navigation :
const CareworkerNavigation = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }} />
<Stack.Screen
name="Main"
options={({ navigation }) => {
return {
headerLeft: () => <Button title="LEFT BUTTON" onPress={() => {
navigation.toggleDrawer(); // <--- this line throws an error
}} />
}
}}
component={DrawerNavigator} />
</Stack.Navigator>
</NavigationContainer>
)
}
export default CareworkerNavigation
Why I can not use navigation.toggleDrawer() in navigation options?
Is is possible to remove this problem ?
If you check the React Navigation docs, "You will need to make the drawer navigator the parent of any navigator where the drawer should be rendered on top of its UI."
React Navigation docs reference
To answer your question : Yes , it is possible.
And here you have a working example:
import React from 'react'
import { Button, View } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import { createDrawerNavigator } from '#react-navigation/drawer'
import { createStackNavigator } from '#react-navigation/stack'
const Feed = () => <View />
const Notifications = () => <View />
const Profile = () => <View />
const FeedStack = createStackNavigator()
const Home = ({ navigation }) => (
<FeedStack.Navigator>
<FeedStack.Screen
name="Feed"
component={Feed}
options={props => {
const { toggleDrawer } = props.navigation // <-- drawer's navigation (not from stack)
return {
headerLeft: () => <Button title="LEFT BUTTON" onPress={toggleDrawer} />
}
}}
/>
</FeedStack.Navigator>
)
const Drawer = createDrawerNavigator()
export default props => {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Feed">
<Drawer.Screen
name="Feed"
component={Home}
options={{ drawerLabel: 'Home' }}
/>
<Drawer.Screen
name="Notifications"
component={Notifications}
options={{ drawerLabel: 'Updates' }}
/>
<Drawer.Screen
name="Profile"
component={Profile}
options={{ drawerLabel: 'Profile' }}
/>
</Drawer.Navigator>
</NavigationContainer>
)
}
While constructing navigation at options, you refer to the navigation of the stack, what cant perform draw actions, try to construct it on header itself
<Stack.Screen
name="Main"
options={() => {
return {
headerLeft: (navigation) => <Button title="LEFT BUTTON" onPress={() => {
navigation.toggleDrawer(); // <--- this line throws an error
}} />
}
}}
component={DrawerNavigator} />
https://github.com/react-navigation/react-navigation/issues/55

Navigation can't find screen if states condition is in Stack.Navigator

When I want to navigate from Home screen to Login screen I get error:
ExceptionsManager.js:173 The action 'NAVIGATE' with payload {"name":"Login"} was not handled by any navigator. Do you have a screen named 'Login'?
routes.js:
import LoginScreen from '../screens/login/login';
import HomeScreen from '../screens/home/home';
const Stack = createStackNavigator();
const App = () => {
const [userToken, setuserToken] = React.useState(null);
React.useEffect(() => {
_bootstrapAsync = async () => {
const token = await AsyncStorage.getItem('token');
if (userToken) {
setuserToken(token)
}
}
_bootstrapAsync()
})
return (
<NavigationContainer>
<Stack.Navigator>
{userToken == null ? (
<Stack.Screen name="Login" component={LoginScreen} />
) : (
<Stack.Screen name="Home" component={HomeScreen} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
home.js:
import { AsyncStorage } from 'react-native';
import { Container, Text, Button, Content } from 'native-base';
const HomeScreen = (props) => {
const { navigation } = props
handleLogout = async () => {
await AsyncStorage.clear();
navigation.navigate('Login');
};
return (
<Container>
<Content>
<Button full onPress={handleLogout}>
<Text>Log Out</Text>
</Button>
</Content>
</Container>
);
}
export default HomeScreen;
If in routes.js I remove userToken states condition, and in Stack.Navigator is only:
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
I can successfully navigate from Home to Login screen.
But this approach is not good, because I need to have checking if token is present in Storage.
How can I solve this issue?
You will not able to find the Login route until your condition is true, that's the reason you are not able to find the Login route
change
{userToken == null ? (
<Stack.Screen name="Login" component={LoginScreen} />
) : (
<Stack.Screen name="Home" component={HomeScreen} />
)}
to
{userToken == null ? (
<Stack.Screen name="Login" component={LoginScreen} />
) : (
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
)}
Hope this helps!