React Native tab navigator nested in stack navigator - javascript

I'm relatively new to React Native and am struggling with screens. I've gone through the nested navigation documentation (https://reactnavigation.org/docs/nesting-navigators/) which helped me setup the initial nav setup i have, but im having some issues.
I'm attempting to setup the app so it has the initial screen as a "Select User" which has no tab navigation. After selecting the user, you are redirected to a another screen which has tab navigation. I currently have it working however I am unable to access any route/props/params after the initial screen.
I've had to manually import navigation with import { useNavigation } from "#react-navigation/native"; and even though I'm providing params in the Navigation.push, trying to access {route} in my screens states that route is undefined.
My setup of the screens looks similar to below:
const Tab = createBottomTabNavigator();
const WelcomeStack = createStackNavigator();
const HomeStack = createStackNavigator();
const SettingsStack = createStackNavigator();
const Stack = createStackNavigator();
const WelcomeStackScreen = () => (
<WelcomeStack.Navigator>
<WelcomeStack.Screen
name="Welcome"
component={WelcomeScreen}
options={{ headerShown: false }}
/>
<WelcomeStack.Screen
name="SignUp"
component={SignUpScreen}
options={{ headerShown: false }}
/>
</WelcomeStack.Navigator>
);
const HomeStackScreen = () => {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={HomeScreen} />
<HomeStack.Screen
name="Search"
component={SearchResultScreen}
options={({ navigation }) => ({
headerLeft: () => (
<Ionicons
name={"arrow-back"}
size={30}
color={colours.text}
style={{ paddingLeft: 15 }}
onPress={() => {
navigation.goBack();
}}
/>
),
})}
/>
<HomeStack.Screen
name="Select"
component={SelectScreen}
options={({ navigation, route }) => ({
title: route.params.name,
headerLeft: () => (
<Ionicons
name={"arrow-back"}
size={30}
color={colours.text}
style={{ paddingLeft: 15 }}
onPress={() => {
navigation.goBack();
}}
/>
),
})}
/>
<HomeStack.Screen name="Read" component={ReaderScreen} />
</HomeStack.Navigator>
);
};
function SettingsScreen() {
return (
<View
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
>
<Text>TBD</Text>
</View>
);
}
const SettingsStackScreen = () => (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
</SettingsStack.Navigator>
);
const TabNavigator = ({ route }) => (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
switch (route.name) {
case "Home":
iconName = focused ? "home" : "home-outline";
break;
case "Settings":
iconName = focused ? "list" : "list-outline";
break;
default:
break;
}
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
tabBarOptions={{
activeTintColor: "tomato",
inactiveTintColor: "gray",
}}
>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsScreen}/>
</Tab.Navigator>
);
export default function App() {
return (
<NavigationContainer theme={navThemeOverride}>
<Stack.Navigator>
<Stack.Screen name="Welcome" component={WelcomeStackScreen} />
<Stack.Screen
name="TabNavigator"
component={TabNavigator}
options={{ headerShown: false }}
navigationOptions={{ gesturesEnabled: false }}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Below is a snippet from the Welcome screen navigation
navigation.push("TabNavigator", {
screen: "Home",
params: {
userId: userId,
},
});

Your Home screen you're trying to navigate to from your parent-tab-navigator ... is also a StackNavigator ... and you wanna navigate to Select screen I guess ... so there's an extra level needed for your navigation to work...
navigation.navigate('TabNavigator', {
screen: 'Home', // <--- StackNavigator
params: {
screen: 'Select', // <-- nested inside HomeStack
params: {
title: 'Your custom title for Select screen here ...',
},
},
});
Plus +
There's a double definition for route in your Tab navigator
const TabNavigator = ({ route }) => ( //<--- here
<Tab.Navigator
screenOptions={({ route }) => ({ // <- and here
Instead
function TabNavigator() {
return <Tab.Navigator screenOptions={({ route }) => ({})>{/* ...Tabs... */}</Tab.Navigator>;
}

Related

Detect internet connection in react native anywhere in screens

I'm confused on how can I show my customDialog whenever I don't have Internet connection to my app, Currently, I've only managed to show my customDialog within LoginScreen , I just want to show my custom dialog from different screen not only on LoginScreen whenever I don't have Internet connection, what is the best way to implement it ? should I put CustomDialog each of the screen?
Someone have the idea to implement this? need help
screens/LoginScreen
import NetInfo from "#react-native-community/netinfo";
import CustomAlert from '../components/CustomAlert';
const LoginScreen = ({navigation}) => {
const [modalVisible, setModalVisible] = useState(false);
const [internetCon, setNet] = useState(true);
React.useEffect(()=>{
checkInternetConnection();
}, [])
function checkInternetConnection() {
NetInfo.addEventListener(state =>{
if (state.isConnected==false){
setModalVisible(true);
setNet(false);
console.log("Connection types", state.type);
console.log("Your device appears to have no internet connectivity. Please check your connection settings and try again");
}
else{
console.log("Connected to internet?", state.isConnected);
}
});
}
return (
<ScrollView style={styles.container} >
{internetCon ===false ? //Check if Internet Existt
<CustomAlert
modalVisible={modalVisible}
setModalVisible={setModalVisible}
title={'Message'}
message={'Your device appears to have no internet connectivity. Please check your connection settings and try again'}
buttons={[{
text: 'Retry',
func: () => {checkInternetConnection();}
}]}
/>
:null}
</ScrollView>
);
};
export default LoginScreen;
navigation/AppStack.js
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const FeedStack = ({navigation}) => (
<Stack.Navigator
screenOptions={{
headerShown: false
}}>
<Stack.Screen
name="test"
component={HomeScreen}
options={{
headerTitleAlign: 'center',
headerTitleStyle: {
color: '#2e64e5',
fontFamily: 'Kufam-SemiBoldItalic',
fontSize: 18,
},
headerStyle: {
shadowColor: '#fff',
elevation: 0,
},
headerRight: () => (
<View style={{marginRight: 10}}>
<FontAwesome5.Button
name="plus"
size={22}
backgroundColor="#fff"
color="#2e64e5"
onPress={() => navigation.navigate('AddPost')}
/>
</View>
),
}}
/>
<Stack.Screen
name="HomeProfile"
component={ProfileScreen}
options={{
title: '',
headerTitleAlign: 'center',
headerStyle: {
backgroundColor: '#fff',
shadowColor: '#fff',
elevation: 0,
},
headerBackTitleVisible: false,
headerBackImage: () => (
<View style={{marginLeft: 15}}>
<Ionicons name="arrow-back" size={25} color="#2e64e5" />
</View>
),
}}
/>
</Stack.Navigator>
);
const AppStack = () => {
const getTabBarVisibility = (route) => {
const routeName = route.state
? route.state.routes[route.state.index].name
: '';
if (routeName === 'Chat') {
return false;
}
return true;
};
return (
<Tab.Navigator
screenOptions={{
headerShown: false
}}
tabBarOptions={{
activeTintColor: '#2e64e5',
}}>
<Tab.Screen
name="Home"
component={FeedStack}
options={({route}) => ({
tabBarLabel: 'Home',
// tabBarVisible: route.state && route.state.index === 0,
tabBarIcon: ({color, size}) => (
<MaterialCommunityIcons
name="home-outline"
color={color}
size={size}
/>
),
})}
/>
<Tab.Screen
name="Messages"
component={MessageStack}
options={({route}) => ({
tabBarVisible: getTabBarVisibility(route),
tabBarIcon: ({color, size}) => (
<Ionicons
name="chatbox-ellipses-outline"
color={color}
size={size}
/>
),
})}
/>
</Tab.Navigator>
);
};
export default AppStack;
navigation/AuthProvider.js
export const AuthContext = createContext();
export const AuthProvider = ({children}) => {
const [user, setUser] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
return (
<AuthContext.Provider
value={{
user,
setUser,
googleLogin: async () => {
try {
// Get the users ID token
const Token = await GoogleSignin.signIn();
const {idToken} = Token;
console.log("Processing");
console.log("ok");
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
await auth().signInWithCredential(googleCredential)
.catch(error => {
console.log('Something went wrong with sign up: ', error);
});
} catch(error) {
// console.log('Something went wrong with sign up: ', error);
}
},
}}>
{children}
</AuthContext.Provider>
);
};
Yo can try react-navigation modal: https://reactnavigation.org/docs/modal/

Need help using goBack() in react native

I am having trouble with the back button in the stack navigator. I am getting an error saying undefined is not an object (evaluating '_this.props'). if it works it goes back to a different tab in the bottom stack navigator. Like if I click on book a session and then I go to home and click back it will take me to book a session no matter what. Please help.
This is what I have so far:
function HomeScreen({ navigation }) {
return (
<WebView
source={{
uri: 'https://www.stoodnt.com/'
}}
style={{ marginTop: -120 }}
/>
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home"
component={HomeScreen}
options={{
headerLeft: () => (
<HeaderBackButton
onPress={() => this.props.navigation.goBack(null)}
/>
),
}}
/>
</HomeStack.Navigator>
);
}
Bottom Navigator:
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-home'
: 'ios-home';
} else if (route.name === 'Book Session') {
iconName = focused ? 'ios-calendar' : 'ios-calendar';
}
else if (route.name === 'Blogs') {
iconName = focused ? 'ios-bookmark' : 'ios-bookmark';
}
else if (route.name === 'Online Courses') {
iconName = focused ? 'ios-book' : 'ios-book';
}
else if (route.name === 'Classes') {
iconName = focused ? 'ios-desktop' : 'ios-desktop';
}
return <Ionicons name={iconName} size={40} color={'orange'} />;
},
})}
tabBarOptions={{
activeTintColor: '#000000',
inactiveTintColor: '#616161',
labelStyle: {
fontSize: 10,
},
style: {
backgroundColor: '#F7F7F7',
},
}}
>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Book Session" component={BookStackScreen} />
<Tab.Screen name="Blogs" component={BlogStackScreen} />
<Tab.Screen name="Online Courses" component={OnlineStackScreen} />
<Tab.Screen name="Classes" component={ClassesStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
try the below code :
function HomeScreen(props) {
return (
<WebView
source={{
uri: 'https://www.stoodnt.com/'
}}
style={{ marginTop: -120 }}
/>
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home"
component={HomeScreen}
options={({ route, navigation }) => ({
headerLeft: () => (
<HeaderBackButton
onPress={() => navigation.goBack(null)}
/>
),
})}
/>
</HomeStack.Navigator>
);
}```
In functional components, you do not have access to "this". You should either pass navigation from the screen options or use useNavigation.
<HomeStack.Screen
...
options={({navigation}) => (
headerLeft: () => (
// you can use navigation.goBack() here
)
)}
or
// this should be added right under function HomeStackScreen,
// right above the return and you will be able to use it in the screens
const navigation = useNavigation()

React Native Navigation Drawer Problem ('navigation.openDrawer' is undefined))

Hi guys i'm new to react native. I want to use drawer navigation with menu button. But actually i don't understand react navigation very well probably. When i press button for openDrawer i'm getting error like this;
TypeError: navigation.openDrawer is not a function. (In 'navigation.openDrawer()', 'navigation.openDrawer' is undefined)
This is My Snack Example ; https://snack.expo.io/#vubes/drawer-check
Here is useful part of my code. Maybe you can understand my problem.
Thanks in advance to anyone who can help.
function DovizStack({navigation}) {
return (
<Stack.Navigator
initialRouteName="Döviz"
screenOptions={{
headerStyle: { backgroundColor: "#1D1D1D" },
headerTintColor: "#fff",
headerTitleStyle: { fontWeight: "bold" },
}}
>
<Stack.Screen
name="Doviz"
component={Doviz}
options={{
title: "Döviz",
headerTitleAlign: "center",
headerLeft: () => (<TouchableOpacity style={{paddingLeft:20}} onPress={()=> navigation.openDrawer()}>
<MaterialCommunityIcons name="menu" color={"white"} size={20} />
</TouchableOpacity>),
}}
/>
<Stack.Screen
name="dovizBuyDetails"
component={dovizBuyDetails}
options={{ title: "Alış", headerTitleAlign: "center" }}
/>
<Stack.Screen
name="dovizSellDetails"
component={dovizSellDetails}
options={{ title: "Satış", headerTitleAlign: "center" }}
/>
</Stack.Navigator>
);
}
I have 5 stack like this. After that comes the myTab.
drawerStack ;
function DrawerStack() {
return(
<Drawer.Navigator initialRouteName="Menu" drawerPosition= "right" >
<Drawer.Screen name="stack" component={stack} />
<Drawer.Screen name="Doviz" component={DovizStack}/>
<Drawer.Screen name="Altın" component={AltinStack} />
</Drawer.Navigator>
)
}
and default stack ;
export default function stack() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
options={{ headerShown: false }}
name="Giriş"
component={LandingStack}
/>
<Stack.Screen
options={{ headerShown: false }}
name="drawer"
component={myTab}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Actually i can run drawer with landingscreen(login page,register) but i don't want to display without login
navigation in not defined in your scope.
use options as function and receive navigation see here.
your code should look like this:
<Stack.Screen
options={({ navigation }) => ({ //receive navigation here
//navigation is defined now you can use it
headerLeft: () => (
<TouchableOpacity
style={{paddingLeft:20}}
onPress={()=> navigation.openDrawer()}>
<MaterialCommunityIcons name="menu" color={"white"} size={20} />
</TouchableOpacity>),
})}
/>
EDIT :
see full example(open drawer menu from nested stack navigation) try snack here
import * as React from 'react';
import { Button, View, Text, TouchableOpacity } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createDrawerNavigator } from '#react-navigation/drawer';
function ProfileScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
</View>
);
}
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
function DovizStack() {
return (
<Stack.Navigator>
<Stack.Screen
name="Profile"
options={({ navigation }) => ({ //receive navigation here
headerLeft: () => (
<TouchableOpacity style={{paddingLeft:20}} onPress={()=> navigation.openDrawer()}>
<Text>open</Text>
</TouchableOpacity>),
})
}
component={ProfileScreen} />
</Stack.Navigator>
);
}
function DrawerStack() {
return(
<Drawer.Navigator initialRouteName="Doviz">
<Drawer.Screen name="Doviz" component={DovizStack}/>
</Drawer.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<DrawerStack/>
</NavigationContainer>
);
}

How to override header title in react navigation 5

In my react native app i have stack navigator and tab navigator like:
const diagnoseNavigation = ({ navigation, language }) => {
return (
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: "#0089d8",
shadowOpacity: 0
},
headerTitleStyle: {
fontWeight: "bold",
color: "white",
textAlign: "center"
},
headerLeft: () => (
.......
)
}}
>
<Stack.Screen
name="Diagnose"
component={tabNav}
options={({ route, navigation }) => {
const routeName = getFocusedRouteNameFromRoute(route) ?? "Home";
switch (routeName) {
case "Screen1": {
return {
headerTitle:"Screen1",
headerRight: () => (
.......
)
};
}
case "Screen2": {
return {
headerTitle: "Screen2" //want to override this one
};
}
default: {
return {
headerTitle: "Home"
};
}
}
}}
/>
</Stack.Navigator>
);
};
Tab navigation:
const Tab = createBottomTabNavigator();
const tabNav = () => {
return (
<Tab.Navigator tabBarOptions={{ showIcon: true }}>
<Tab.Screen
name="Screen1"
component={Screen1Screen}
/>
<Tab.Screen
name="Screen2"
component={Screen2Screen}
/>
<Tab.Screen
name="Screen3"
component={recommendedNavigation}
/>
</Tab.Navigator>
);
};
Screen2
const Screen2Screen = ({ route, language }) => {
return (
<Stack.Navigator>
<Stack.Screen options={{ headerShown: false }} name="A" component={AScreen} />
<Stack.Screen
name="B"
component={BScreen}
options={() => {
return {
headerTitle: "ABC" //with this one
};
}}
/>
</Stack.Navigator>
);
};
In short i have stack navigatior, in that i have tab navigator. there are 3 tabs,what i want is in my tab 2(Screen2) there is a button when i click that button it should navigate to another page within the same tab.Then the title should be the "ABC" but it still shows "Screen2" under that ABC. ie, not overriding the header title?
Something like this:
You have to pass showLabel props to hide the header in your tababrOptions.
<Tab.Navigator tabBarOptions={{ showIcon: true , showLabel: false,
}}>

Header title not changing with bottom navigation React Native Navigation v5

so I 've got nested navigation, as the code shows. Now I am trying to make the header bar. however, when I change the screens from bottom navigation the header remains the same, no matter on what screen I am(always shoes that I am on Browser screen). Also is the React navigation header the best to create a header bar or is better to create a fully custom one. Thank you
I am expecting that the header title will change in accordance with the page.
const Stack = createStackNavigator();
const Tabs = createMaterialBottomTabNavigator();
const AuthStack = createStackNavigator();
const TabsScreen = () => (
<Tabs.Navigator shifting={true} initialRouteName="Home" activeColor="#fff">
<Tabs.Screen
name="Home"
component={Browser}
options={{
tabBarLabel: 'Home',
tabBarColor: '#009387',
tabBarIcon: ({color}) => (
),
}}
/>
<Tabs.Screen
name="Sleep"
component={Sleep}
options={{
tabBarLabel: 'Sleep',
tabBarColor: '#694fad',
tabBarIcon: ({color}) => (
),
}}
/>
<Tabs.Screen
name="Settings"
component={SettingWithContext}
options={{
tabBarLabel: 'Settings',
tabBarColor: '#694fad',
tabBarIcon: ({color}) => (
),
}}
/>
</Tabs.Navigator>
);
export default CreateStack = () => {
const [isLoading, setIsLoading] = React.useState(true);
const [userToken, setUserToken] = React.useState(null);
const authContext = React.useMemo(() => {
return {
signIn: () => {
setIsLoading(false);
setUserToken('asdf');
},
signUp: () => {
setIsLoading(false);
setUserToken('asdf');
},
signOut: () => {
setIsLoading(false);
setUserToken(null);
},
};
}, []);
React.useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 1000);
}, []);
if (isLoading) {
return ;
}
return (
<AuthContext.Provider value={authContext}>
{userToken ? (
<Stack.Navigator initialRouteName={Browser}>
<Stack.Screen name="Browser" component={TabsScreen} />
<Stack.Screen name="PreScreen" component={PreScreen} />
<Stack.Screen name="Player" component={Player} />
</Stack.Navigator>
) : (
<AuthStack.Navigator
screenOptions={{headerShown: false}}
initialRouteName={RegisterLogin}>
<AuthStack.Screen name="RegisterLogin" component={RegisterLogin} />
<AuthStack.Screen name="Login" component={LoginWithContext} />
<AuthStack.Screen name="Register" component={RegisterWithContext} />
</AuthStack.Navigator>
)}
</AuthContext.Provider>
);
};
If you want to have every tab name to be the top try this: https://reactnavigation.org/docs/screen-options-resolution/
Check out the getHeaderTitle function and the switch statement below that - it helped me achieve this behavoir.
Because you are using nested navigation, and the stack navigator is the parent one, the top header will remain the same for any navigator inside the TabNavigator.
You might want to create instead a stack navigator inside each tab, so each tab will have its own header title and navigation path.
In another hand, you could try using screen options in order to create some business logic to determine which title you want based on the navigation state.
For example:
const Stack = createStackNavigator();
const Tabs = createMaterialBottomTabNavigator();
const AuthStack = createStackNavigator();
const TabsScreen = () => (
<>
<Tabs.Navigator shifting={true} initialRouteName="Home" activeColor="#fff">
<Tabs.Screen
name="Home"
component={Browser}
options={{
tabBarLabel: "Home",
tabBarColor: "#009387",
tabBarIcon: ({ color }) => color,
}}
/>
<Tabs.Screen
name="Sleep"
component={Sleep}
options={{
tabBarLabel: "Sleep",
tabBarColor: "#694fad",
tabBarIcon: ({ color }) => color,
}}
/>
<Tabs.Screen
name="Settings"
component={SettingWithContext}
options={{
tabBarLabel: "Settings",
tabBarColor: "#694fad",
tabBarIcon: ({ color }) => color,
}}
/>
</Tabs.Navigator>
</>
);
let CreateStack = () => {
const [isLoading, setIsLoading] = React.useState(true);
const [userToken, setUserToken] = React.useState(null);
const authContext = React.useMemo(() => {
return {
signIn: () => {
setIsLoading(false);
setUserToken("asdf");
},
signUp: () => {
setIsLoading(false);
setUserToken("asdf");
},
signOut: () => {
setIsLoading(false);
setUserToken(null);
},
};
}, []);
React.useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 1000);
}, []);
if (isLoading) {
return;
}
return (
<AuthContext.Provider value={authContext}>
{userToken ? (
<Stack.Navigator initialRouteName={Browser}>
{/* Added the options prop to the Browser screen*/}
<Stack.Screen
name="Browser"
component={TabsScreen}
options={({ route }) => ({
headerTitle: getHeaderTitle(route),
})}
/>
<Stack.Screen name="PreScreen" component={PreScreen} />
<Stack.Screen name="Player" component={Player} />
</Stack.Navigator>
) : (
<AuthStack.Navigator
screenOptions={{ headerShown: false }}
initialRouteName={RegisterLogin}
>
<AuthStack.Screen name="RegisterLogin" component={RegisterLogin} />
<AuthStack.Screen name="Login" component={LoginWithContext} />
<AuthStack.Screen name="Register" component={RegisterWithContext} />
</AuthStack.Navigator>
)}
</AuthContext.Provider>
);
};
/**
* Method to get the route name based on the component.
*/
const getHeaderTitle = (route) => {
if (route === "Browser") {
return "Home";
} else if (route === "Sleep") {
return "Sleep"
} else if (route === "Settings") {
return "Settings";
} else {
return route;
}
}
export default CreateStack;
The header is a thing in the stack navigation, not however in the tab-navigation.
Your App is using the header of the stack-navigator in which the tab-navigator is nested.
You could nest stack navigators inside your Tab-Navigator to have the header change as you need it to.

Categories

Resources