React Native navigation error only when user is logged in - react-native

I have an option to browse a welcome screen not logged in, or logged in (and it shows more features). I thought I had the navigation configured correctly but I guess not. The error "The action NAVIGATE with payload... was not handled by any navigator" is leading me to believe it's because i'm using one navigator nested within another, BUT the weird thing is I do NOT get this error at all if the user is not logged in. It only happens when they are. The main button calls this signOut() function to either log them out and return to the main screen, or just return to the main screen if they aren't logged in. Did I not do this correctly? Or is it still likely to be my nested navigation problem in which case, why would it only give the error if the user is logged in?
function signOut(){
if (user){
auth
.signOut()
.then(() => navigation.navigate('StartScreen'));
} else {
navigation.navigate('StartScreen');
}
}
here is my AuthNavigator:
const Stack = createStackNavigator();
export default function AuthNavigator(){
// Set an initializing state whilst Firebase connects
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState();
// Handle user state changes
function onAuthStateChanged(user) {
setUser(user);
if (initializing) setInitializing(false);
}
useEffect(() => {
const subscriber = auth.onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
if (initializing) return null;
if (user) {
return (
<MainHeader />
);
}
return (
<Stack.Navigator>
<Stack.Screen name="StartScreen" component={StartScreen} options={{ headerShown: false }} />
<Stack.Screen name="SignIn" component={SignIn} options={{ headerShown: false }} />
<Stack.Screen name="SignUp" component={SignUp} options={{ headerShown: false }} />
<Stack.Screen name="MainHeader" component={MainHeader} options={{ headerShown: false }} />
<Stack.Screen name="ForgotPassword" component={ForgotPassword} options={{ headerShown: false }} />
</Stack.Navigator>
)
}
And here is my nested Tab Navigator within the MainHeader screen. The WelcomeScreen has the logout button that performs the signOut() function I first listed:
export default function MainHeader() {
var user = auth.currentUser;
return (
<>
<View style={styles.headerContainer}>
<Image
style={styles.image}
source={require("../assets/newheader4.png")}
/>
</View>
<Tab.Navigator
tabBarOptions={{
activeTintColor: "blue",
inactiveTintColor: "black",
style: {},
tabStyle: {
width: "auto",
backgroundColor: "#e0d5f3",
borderTopWidth: 3,
borderBottomWidth: 3,
borderRightColor: "gray",
},
labelStyle: {
fontSize: 14,
fontWeight: "bold",
},
scrollEnabled: true,
}}
>
<Tab.Screen name="Home" component={WelcomeScreen} />
<Tab.Screen name="All Deals" component={MessagesScreen} />
<Tab.Screen name="My Favs" component={FavoritesScreen} />
<Tab.Screen name="Notifications" component={Notifications} />
<Tab.Screen name="Free Samples" component={FreeScreen} />
<Tab.Screen name="Who We Are" component={WhoWeAreScreen} />
</Tab.Navigator>
</>
);
}

Solution: remove the navigation line completely from the then() after auth.signOut. It does it by default and redirects to the StartScreen perfectly without it.
New signOut function:
function signOut(){
if (user){
auth
.signOut()
.then();
} else {
navigation.navigate('StartScreen');
}
}

Related

How to replace tab navigation screen to stack navigation screen in react native

I am using stack navigation for the login module, When login is successful after that I navigate to the tab navigation (Home screen). When the user goes to log out from the profile screen after that I am not able to navigate on stack navigation (login screen).
I am searching on google but still have not found any related answer
this is the navigation screen code:-
const Stack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();
function Navigation() {
const [token, setToken] = useState('');
const [sessionHandle, setSessionHandle] = useState(false);
AsyncStorage.getItem('token', (err, result) => {
setToken(result);
if (!token) {
setSessionHandle(false);
} else {
setSessionHandle(true);
}
});
const {width, height} = Dimensions.get('window');
function MyTabs() {
return (
<Tab.Navigator
initialRouteName="HomeTab"
screenOptions={{
headerShown: false,
tabBarActiveTintColor: Colors.black,
tabBarInactiveTintColor: Colors.gray,
tabBarStyle: {height: width * 0.18, borderRadius: 40},
}}>
<Tab.Screen
name="HomeTab"
component={ProductFlowNavigation}
options={{
tabBarLabel: 'Home',
tabBarLabelStyle: {
fontSize: 12,
fontFamily: 'Poppins-Regular',
marginBottom: 10,
},
tabBarIcon: ({focused}) => {
const set_color = focused ? Colors.bright_sky_blue : Colors.gray;
return <Ionicons name="ios-home" color={set_color} size={25} />;
},
}}
/>
<Tab.Screen
name="OrderHistory"
component={OrderHistory}
options={{
tabBarLabel: 'History',
tabBarLabelStyle: {
fontSize: 12,
fontFamily: 'Poppins-Regular',
marginBottom: 10,
},
tabBarIcon: ({focused}) => {
const set_color = focused ? Colors.bright_sky_blue : Colors.gray;
return <Ionicons name="timer" color={set_color} size={25} />;
},
}}
/>
<Tab.Screen
name="Profile"
component={Profile}
options={{
tabBarLabel: 'Profile',
tabBarLabelStyle: {
fontSize: 12,
fontFamily: 'Poppins-Regular',
marginBottom: 10,
},
tabBarIcon: ({focused}) => {
const set_color = focused ? Colors.bright_sky_blue : Colors.gray;
return <Ionicons name="person" color={set_color} size={25} />;
},
}}
/>
</Tab.Navigator>
);
}
function MyLoginSignupNavigation() {
return (
<Stack.Navigator initialRouteName="Login">
<Stack.Screen
name="Signup"
component={SignUp}
options={{headerShown: false}}
/>
<Stack.Screen
name="Login"
component={Login}
options={{headerShown: false}}
/>
<Stack.Screen
name="Home"
component={MyTabs}
options={{headerShown: false}}
/>
</Stack.Navigator>
);
}
return (
<NavigationContainer>
{sessionHandle ? <MyTabs /> : <MyLoginSignupNavigation />}
</NavigationContainer>
);
}
export default Navigation;
#mahesh you are switching between tab and stack navigation based on "sessionHandle", so i suggest you if you want to logout just set the value of "sessionHandle" to false, it will direct you to stack navigation first screen
{sessionHandle ? <MyTabs /> : <MyLoginSignupNavigation />}
you navigation depends on sessionHandle
Hopefully it will help you

React Navigation Authentication Flow: The action 'NAVIGATE' with payload {"name":"HomeScreen"} was not handled by any navigator

A beginner at React Native here, trying to combine this doc with AWS Amplify Authentication to implement React Navigation Authentication Flow but I can't seem to figure out what's wrong. Whenever I click on the login button, this error appears.
Navigation Code (excluding imports):
const NavigationGeneral = () => {
const [user, setUser] = useState(undefined);
const checkUser = async () => {
try {
const authUser = await Auth.currentAuthenticatedUser({bypassCache: true});
setUser(authUser);
} catch (e) {
setUser(null);
}
}
useEffect(() => {
checkUser();
}, []);
if (user === undefined) {
return (
<View style = {{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator />
</View>
)
}
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
{user ? (
<>
<Stack.Screen name = "Login" component = {LoginScreen} />
<Stack.Screen name = "SignUp" component = {SignUpScreen} />
<Stack.Screen name = "ConfirmEmail" component = {ConfirmEmailScreen} />
<Stack.Screen name = "ForgotPassword" component = {ForgotPasswordScreen} />
<Stack.Screen name = "NewPassword" component = {NewPasswordScreen} />
</>
): (
<Stack.Screen name = "HomeScreen" component = {HomeTabNavigator} />
)}
</Stack.Navigator>
);
};
//* TAB NAVIGATOR FOR APP SCREENS
const HomeTabNavigator = () => {
return (
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
tabBarStyle: {backgroundColor: '#0052cc'},
tabBarInactiveTintColor: '#fff',
tabBarActiveTintColor: '#fff',
tabBarActiveBackgroundColor: '#006600'
}}>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color, size }) => (
<Ionicons name="home-outline" color={'#fff'} size={25} />
),
}}
/>
<Tab.Screen
name="Courses"
component={CourseScreen}
options={{
tabBarLabel: 'Courses',
tabBarIcon: ({ color, size }) => (
<Ionicons name="library-outline" color={'#fff'} size={25} />
),
}}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{
tabBarLabel: 'My Profile',
tabBarIcon: ({ color, size }) => (
<Ionicons name="person-outline" color={'#fff'} size={25} />
),
}}
/>
<Tab.Screen
name="Forensic Tools"
component={ForensicToolsScreen}
options={{
tabBarLabel: 'Tools List',
tabBarIcon: ({ color, size }) => (
<Ionicons name="list-outline" color={'#fff'} size={25} />
),
}}
/>
<Tab.Screen
name="Leaderboard"
component={LeaderboardScreen}
options={{
tabBarLabel: 'Leaderboard',
tabBarIcon: ({ color, size }) => (
<Ionicons name="podium-outline" color={'#fff'} size={25} />
),
}}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{
tabBarLabel: 'Settings',
tabBarIcon: ({ color, size }) => (
<Ionicons name="settings-outline" color={'#fff'} size={25} />
),
}}
/>
</Tab.Navigator>
);
};
export default NavigationGeneral;
LoginScreen.js code snippet:
const onLoginPressed = async data => {
if (loading) {
return;
}
setLoading(true);
try {
await Auth.signIn(data.username, data.password);
navigation.navigate("HomeScreen");
console.log("Login");
} catch (e) {
Alert.alert('Oops', e.message);
}
setLoading(false);
};
I understand that I'm not supposed to manually navigate using navigation.navigate() but rather to conditionally define the screens. But whenever I remove navigation.navigate("HomeScreen"); from the code, nothing happens when I press the login button. So I assume something is wrong with my conditioning in my NavigationGeneral code, I just can't seem to figure out the problem.
Some help or additional tips would be greatly appreciated, thanks in advance. Please let me know if more info is required.
When you try to navigate to the HomeScreen I assume the user is not yet set in the state and therefore the screen with the name HomeScreen does not exist yet, so the navigator has nowhere to go.
Try setting the user in the NavigationGeneral upon login, it should event redirect automatically without using navigation.navigate.
You should not trigger navigation manually when implementing authentication flow with react-navigation. After the successful login, user will be truthy value. This means conditional rendering inside of navigation will handle it automatically.
Moreover, in case of condition user===undefined do not render LoadingIndicator, instead, create a new loading state and render LoadingIndicator when the state loading becomes true.
And I believe it should be !user instead of user, since not authenticated user (user===undefined) will want to see Login, Signup etc. screens.
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
// Here it should be !user
{!user ? (
<>
<Stack.Screen name = "Login" component = {LoginScreen} />
<Stack.Screen name = "SignUp" component = {SignUpScreen} />
<Stack.Screen name = "ConfirmEmail" component = {ConfirmEmailScreen} />
<Stack.Screen name = "ForgotPassword" component = {ForgotPasswordScreen} />
<Stack.Screen name = "NewPassword" component = {NewPasswordScreen} />
</>
): (
<Stack.Screen name = "HomeScreen" component = {HomeTabNavigator} />
)}
</Stack.Navigator>
);
const [checkAuthUserLoading, setCheckAuthUserLoading] = React.useState(false);
const user = useSelector((state) => state.user); // Retrieve user from redux store. (It can be mobx, zustand or any other state management.)
const checkUser = async () => {
try {
setCheckAuthUserLoading(true);
const authUser = await Auth.currentAuthenticatedUser({
bypassCache: true,
});
setCheckAuthUserLoading(false);
dispatch(setAuthUser(user)); // I would suggest to use state management such as redux or mobx instead of storing user in component state
} catch (e) {
dispatch(setAuthUserError(e)); // Here you do not need to setUser(undefined) since its already failed and will be stay as undefined
}
};
if (checkAuthUserLoading) {
return <LoadingIndicator />;
}

Always show BottomTabNavigation

How can I show BottomTabNavigation even on stacked screen? I have tried this for a few hours but really don't get it to work as expected.
So the thing I want to happen is, if I navigate to say for example the Title Screen, I still want to show the BottomTabNavigation. Any suggestions?
I can of course create a new navigation, but then it is sliding in from the side.
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const HomeTabNavigator = () => {
return (
<Tab.Navigator
tabBarOptions={{
labelStyle: {textTransform: 'uppercase'},
style: {
backgroundColor: '#111111', //Färger på footerbar
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
},
}}>
<Tab.Screen
name={'Concerts'}
component={ConcertsScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image
source={require('../../assets/icons/concerts.png')}
size={25}
/>
),
}}
/>
<Tab.Screen
name={'Docs'}
component={DocumentiesScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/docs.png')} size={25} />
),
}}
/>
<Tab.Screen
name={'Profile'}
component={ProfileScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
}}
/>
</Tab.Navigator>
);
};
const Router = () => {
const {token, setToken} = useContext(TokenContext);
const {userFav, addFav, getFav} = useContext(UserContext);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
setTimeout(() => {}, 1000);
}, []);
return (
<NavigationContainer>
{token ? (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerTransparent: true,
noBorder: true,
}}
headerMode="float">
<Stack.Screen name={' '} component={HomeTabNavigator} />
<Stack.Screen name={'Concerts'} component={ConcertsScreen} />
<Stack.Screen name={'User Profile'} component={ProfileScreen} />
<Stack.Screen
name={'FavouritesScreen'}
component={FavouritesScreen}
/>
<Stack.Screen name={'Docs'} component={DocumentiesScreen} />
<Stack.Screen name={'AccountScreen'} component={AccountScreen} />
<Stack.Screen name={'Home of'} component={SearchScreen} />
<Stack.Screen name={'Artist'} component={ArtistScreen} />
<Stack.Screen name={'Title'} component={Videos} />
<Stack.Screen name={'PlayVideo'} component={PlayVideo} />
</Stack.Navigator>
) : (
<LoginScreen />
)}
</NavigationContainer>
);
};
You need to nest all your stack screens inside a tab screen.
The BottomTabNavigator disappear because you leave your Tab.Navigator component.
I hope this helps. If you want to navigate between screens that are related to a specific tab button, and have that tab button remain active while moving between these screens, you should set up a StackNavigation within that tab's component. By doing so, the tab button will remain active while navigating within its related screens.
On the other hand, if you want the TabNavigation to be visible throughout the whole application but some screens should not be displayed as tabs, you can add all screens inside the TabNavigation and specify in the options for those screens not to be displayed as tab buttons. That way, while in the screen without a tab button, the tabs will still be visible but none will be active. For example, you can do this for a screen called 'Title':
<Tab.Screen
name={'Title'}
component={Videos}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
tabBarButton: () => null <---- *this causes it to have no button*
}}
/>
I hope this helps!

'focus' listener not calling every time. React Native

I am new in react native technology. I have used following hierarchy in my react native app.
I have used Stack.Navigator in which I am using various screen, I. Login, II. Sign-Up, III. TabBar Screen.
In TabBar screen I have used various individual Stack.Navigator because I have to show tab bar on every child screen.
Now problem is that I have to update some value on tab screen when user click on that particular screen. It is calling once at first time, not working second time.
I have used following code for execute 'focus' listener on tab screen :-
onScreenFocus = () => {
this.reloadFavrouite()
alert("again calling")
}
componentDidMount() {
this.props.navigation.addListener('focus', () => this.onScreenFocus())
this.reloadFavrouite()
}
'focus' listener work well If I am not use individual Stack.Navigator at every tab screen, but it is creating another issue is that tab bar not showing in child screen.
I have used this tab navigator code:- Please check
export default class TabNavigator extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Tab.Navigator initialRouteName='Home' lazy={true} tabBarOptions={
{
style: {
backgroundColor: '#FFFFFF',
borderTopLeftRadius: 25,
borderTopRightRadius: 25,
overflow: 'hidden',
borderColor: 'transparent',
}
}
}>
<Tab.Screen name="HomeNavigator" component={HomeNavigator}
options={{
tabBarLabel: ({ focused }) => {
const tintColortab = focused ? "#006C35"
: "#000000"
return (
<Text style={{ color: tintColortab, fontSize: 8, fontFamily: "Roboto-Bold", marginBottom: 6 }}>{StringsOfLanguages.Home}</Text>
)
},
tabBarIcon: ({ focused }) => {
const tintColortab = focused ? "#006C35"
: "#000000"
return (
<Image source={require('../../../assets/images/TabBarIcons/ic_home_selected/ic_home_selected.png')}
style={{ marginTop: 10, tintColor: tintColortab }}>
</Image>
)
}
}}>
</Tab.Screen>
<Tab.Screen name="FavouriteNavigator" component={FavouriteNavigator}
options={{
tabBarLabel: ({ focused }) => {
const tintColortab = focused ? "#006C35"
: "#000000"
return (
<Text style={{ color: tintColortab, fontSize: 8, fontFamily: "Roboto-Bold", marginBottom: 6 }}>{StringsOfLanguages.Favorite}</Text>
)
},
tabBarIcon: ({ focused }) => {
const tintColortab = focused ? "#006C35"
: "#000000"
return (
<Image source={require('../../../assets/images/TabBarIcons/ic_favorits_unselected/ic_favorite_unselected.png')}
style={{ marginTop: 10, tintColor: tintColortab }}>
</Image>
)
}
}}
>
</Tab.Screen>
</Tab.Navigator>
);
}
And individual navigation is like :-
class HomeNavigator extends React.Component {
render() {
return (
<NavigationContainer independent={true}>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} options={{ headerShown: false }} />
<Stack.Screen name="AllRestuarent" component={AllRestuarent} options={{ headerShown: false }} />
<Stack.Screen name="AllLaundry" component={AllLaundry} options={{ headerShown: false }} />
<Stack.Screen name="AllEntertainment" component={AllEntertainment} options={{ headerShown: false }} />
<Stack.Screen name="Offer" component={Offer} options={{ headerShown: false }} />
<Stack.Screen name="ComponyMap" component={ComponyMap} options={{ headerShown: false }} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
class FavouriteNavigator extends React.Component {
render() {
return (
<NavigationContainer independent={true}>
<Stack.Navigator>
<Stack.Screen name="Favorites" component={Favorites} options={{ headerShown: false }} />
<Stack.Screen name="Offer" component={Offer} options={{ headerShown: false }} />
<Stack.Screen name="ComponyMap" component={ComponyMap} options={{ headerShown: false }} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
You are not using focus listener in the right way according to your current version, You are using react-navigation v5 and you are adding a listener the way it is used in react-navigation v4, Here is a solution to your problem.
class YourClassComopnent extends React.Component {
componentDidMount() {
/*
Now you can access to isFocused prop and
this prop will be true whenever your screen is
in the focus.
*/
if(this.props.isFocused) this.onScreenFocus()
this.reloadFavrouite()
}
render() {
// Get it from props
const { isFocused } = this.props;
}
}
// Wrap and export
export default function(props) {
const isFocused = useIsFocused();
// pass your class here
return <YourClassComopnent {...props} isFocused={isFocused} />;
}
Link to docs, how you can use isFocused in react-navigation v5

React Native Navigation v5 bottom navigation navigate to new stack

Learning React Native and I've run into a navigation issue using createBottomTabNavigator.
I have my screen displayed with 2 links on the bottom tab. Link 1 - Novels, Link 2 - Profile.
When I click on Link 2, I want to go to my profile screen (which it does) but I want to replace the bottom tab with a new one (which it doesn't).
I've tried using the tabPress event and I can see using console.log that it catches the event, but when I add the navigation param it stops working.
here's the relevant code:
const Stack = createStackNavigator();
const BottomTab = createBottomTabNavigator();
const headerTitle = "My Title";
function NovelsStack() {
return (
<Stack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#000',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
fontSize: 16,
},
}}>
<Stack.Screen name="Home" component={HomeScreen} options={{ headerTitle: () => <HeaderTitle title={headerTitle} /> }} />
<Stack.Screen name="Novels" component={TabNavigation} options={{ headerTitle: () => <HeaderTitle title={headerTitle} /> }} />
<Stack.Screen name="Novel" component={SelectedNovelNavigation} options={{ headerTitle: () => <HeaderTitle /> }} />
<Stack.Screen name="Profile" component={ProfileNavigation} options={{ headerTitle: () => <HeaderTitle title={headerTitle} /> }} />
</Stack.Navigator>
);
}
function TabNavigation() {
return (
<BottomTab.Navigator
tabBarOptions={{
labelStyle: styles.mainTabBarLabels
}}
>
<BottomTab.Screen name="Novels" options={{ title: "Novels" }} component={NovelsScreen} />
{isAuthenticatedUser() ? (<BottomTab.Screen name="Profile" component={ProfileScreen} />)
: (<BottomTab.Screen name="Login" component={LoginScreen} listeners={({ navigation, route }) => ({
tabPress: e => {
// Prevent default action
console.log(navigation)
e.preventDefault();
},
})} />)
}
</BottomTab.Navigator>
);
}
function ProfileNavigation() {
return (
<BottomTab.Navigator
tabBarOptions={{
labelStyle: styles.novelsTabBarLabels
}}>
<BottomTab.Screen name="Profile" component={ProfileScreen} options={{ title: "Profile" }} />
</BottomTab.Navigator>
);
}
function SelectedNovelNavigation() {
return (
<BottomTab.Navigator
tabBarOptions={{
labelStyle: styles.novelsTabBarLabels
}}>
<BottomTab.Screen name="Novel" component={NovelScreen} />
<BottomTab.Screen name="Comments" component={CommentsScreen} options={{ title: "Comments" }} />
<BottomTab.Screen name="Ratings" component={RatingsScreen} options={{ title: "Ratings" }} />
<BottomTab.Screen name="Related" component={RelatedNovelsScreen} options={{ title: "Related" }} />
</BottomTab.Navigator>
)
}
What I want to happen is when the user presses the "Profile" tab on the TabNavigation Stack, that the navigation takes the user to show the ProfileNavigation where I can add additional profile tabs, but I just can't get that hooked up correctly. Been looking at the docs and other posts about it, but still stuck.
Thanks in advance for any help
As usual, once you reach out for help you get the answer. In the docs here (https://reactnavigation.org/docs/bottom-tab-navigator/) and here (https://reactnavigation.org/docs/stack-actions/#replace) I can customize the tabBar and use the navigation.replace.
The good ole docs, I was all around it, just didn't see it.