I tried following React-Navigation tutorial to implement Stack Navigator Within a Tab Navigator in React Native application using expo.
I am trying to achieve the following
RootStack (Stack Navigator)
Home (Screen)
Login (Screen)
ConsumerApp (Tab Navigator)
SettingsStackScreen (Stack Navigator)
Settings (Screen)
Details (Screen)
BusinessApp
Orders (screen)
Even when following the tutorial and using the code defined there (as you can see from SettingsStackScreen) I am getting the following error printed over and over again. The BusinessApp works great, because it doesn't have Stack Navigators defined within it, only pure component screen.
ERROR Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
in PreventRemoveProvider (created by NavigationContent)
in NavigationContent
in Unknown (created by NativeStackNavigator)
in NativeStackNavigator (created by SettingsStackScreen)
in SettingsStackScreen (created by SceneView)
I tried creating the most minimal example possible, so I wasn't sure what else to change/try.
This is root stack navigator in App.js
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} options={{ headerShown: false }} />
<Stack.Screen name="Login" component={LoginScreen} options={{ title: t('Login-s') }}/>
<Stack.Screen name="ConsumerApp" component={ConsumerAppScreen} options={{headerShown: false}}/>
<Stack.Screen name="BusinessApp" component={BusinessAppScreen} options={{headerShown: false}}/>
</Stack.Navigator>
</NavigationContainer>
);
}
The ConsumerAppScreen:
const Tab = createBottomTabNavigator();
export const ConsumerAppScreen = () => {
const { t, i18n } = useTranslation();
function DetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function SettingsScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
const SettingsStack = createNativeStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Details" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
return (
<Tab.Navigator
initialRouteName="Settings"
tabBar={(props) => <ConsumerTabBar {...props} />}
>
<Tab.Screen name="Settings" component={SettingsStackScreen} options={{ icon: require("../../assets/dashboard-icon.png"), title: t('Settings') }} />
</Tab.Navigator>
)
}
It's because the way you created ConsumerAppScreen is not correct. Screen components and StackNavigator declarations shouldn't be inside a react component.
(I think the tab bar should have more than one child, but I'm not sure about it.)
The correct implementation:
function DetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function SettingsScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
const SettingsStack = createNativeStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Details" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
// tab navigation
const Tab = createBottomTabNavigator();
export const ConsumerAppScreen = () => {
const { t, i18n } = useTranslation();
return (
<Tab.Navigator
initialRouteName="Settings"
tabBar={(props) => <ConsumerTabBar {...props} />}
>
<Tab.Screen name="Settings" component={SettingsStackScreen} options={{ icon: require("../../assets/dashboard-icon.png"), title: t('Settings') }} />
</Tab.Navigator>
)
}
Related
I created a background component with a picture. I want to use it in Navigator for all screens but I get error Error: A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'BackgroundImage'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.
return (
<BackgroundImage>
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: Colors.header },
headerTintColor: 'white',
contentStyle: { backgroundColor: Colors.primary100 },
}}
>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
</Stack.Navigator>
</BackgroundImage>
);
}
My component
const image = require('../assets/background/home.png');
const BackgroundImage = ({children}) => (
<ImageBackground source={image} style={styles.image}>
{children}
</ImageBackground>
);
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
},
image: {
flex: 1,
justifyContent: 'center'
}
});
export default BackgroundImage;
I this case is works
function HomeScreeen() {
return (
<BackgroundImage>
<View style={styles.rootContainer}>
<Image source={logo} />
<Text style={styles.title}>Test!</Text>
</View>
</BackgroundImage>
);
}
#superDev1117 I use NavigationContener in this file and I get "Error: Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitly. Note that this will make the child navigators disconnected from the parent and you won't be able to navigate between them."
function Navigation() {
const authCtx = useContext(AuthContex);
return (
<NavigationContainer>
{!authCtx.isAuthenticated && <AuthStack />}
{authCtx.isAuthenticated && <AuthenticatedStack />}
</NavigationContainer>
You can't use BackgroundImage component in this case.
Try with this:
on Root component,
<BackgroundImage>
<NavigationContainer> // from '#react-navigation/native';
<Stack.Navigator
...
/>
</NavigationContainer>
</BackgroundImage>
I am a beginner in react native. I have gone through different related topics. But failed. This is my issues,
I have a bottom navigator with 4 items, say Dashboard, X, Patient and Y. Here is the optimised bottom navigator code.
const Stack = createStackNavigator();
const Bottom = createBottomTabNavigator();
const Main = () => {
return (
<Bottom.Navigator
initialRouteName="DashboardScreenStack"
tabBarOptions={{
style: {
height: 70,
paddingTop: 20,
backgroundColor: '#F3F6FF',
},
activeTintColor: colors.navigationTextActive,
inactiveTintColor: colors.navigationTextInactive,
labelStyle: {
fontSize: 15,
marginTop: 15,
paddingBottom: 10,
},
}}>
<Bottom.Screen
name="DashboardScreenStack"
component={DashboardScreenStack}
options={{
tabBarLabel: 'Dashboard',
}}
/>
<Bottom.Screen
name="X"
component={X}
options={{
tabBarLabel: 'X',
}}
/>
<Bottom.Screen
name="Patient"
component={Patient}
options={{
tabBarLabel: 'Patient',
}}
/>
<Bottom.Screen
name="Y"
component={Y}
options={{
tabBarLabel: 'Y',
}}
/>
</Bottom.Navigator>
);
};
This is my code for Patient menu.
const Patient = (props) => {
let resultData = null;
var initialRoute = '';
if (
typeof props.route.params != 'undefined' &&
props.route.params.result != null
) {
resultData = props.route.params.result;
initialRoute = 'PatientDashboardScreen';
} else {
initialRoute = 'AddPatientScreen';
}
return (
<Stack.Navigator
initialRouteName={initialRoute }>
<Stack.Screen
name="PatientDashboardScreen"
component={PatientDashboardScreen}
initialParams={resultData}
options={{headerShown: false}}
/>
<Stack.Screen
name="TestScreen1"
component={TestScreen1}
options={{headerShown: false}}
/>
<Stack.Screen
name="TestScreen2"
component={TestScreen2}
options={{headerShown: false}}
/>
<Stack.Screen
name="AddPatientScreen"
component={AddPatientScreen}
options={{headerShown: false}}
/>
</Stack.Navigator>
);
};
There are 4 screens that should be shown in Patient menu. Out of those if I am selecting an item in my Dashboard menu I need to open "PatientDashboardScreen". And there will be some data available in props too. But on directly clicking 'Patient' menu, I need to move to "AddPatientScreen" where no data is passed.
I tried the above code. But only the initial click works. If I am selecting from list first, the always Patient menu is showing "PatientDashboardScreen" and if I am selecting Patient menu directly first, then always "AddPatientScreen" is shown on Patient menu selection.
Any help would be greateful. Thank you
Based on your question
You have a bottom navigator and one of the screens has a nested stack navigator.
The requirement here is to show a specific screen when pressing the bottom navigator button and redirect to a screen when opening from another screen in bottom navigator along with some parameters.
This is one way to do this.
<Tab.Screen
name="Hospital"
component={HospitalView}
options={({ navigation }) => ({
tabBarButton: (props) => (
<TouchableOpacity
{...props}
onPress={() =>
navigation.navigate('Hospital', { screen: 'Patient' })
}
/>
),
})}
/>
You can have a custom onPress in your bottom navigator button which will use the navigate with the screen option which will take you to the specific screen.
To navigate from another screen with parameters you can use the below option
<Button
title="Doctor"
onPress={() =>
navigation.navigate('Hospital', {
screen: 'Doctor',
params: { name: 'Doc 1' },
})
}
/>
Your full code should look something similar to this
const Stack = createStackNavigator();
function Patient() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Patient Screen</Text>
</View>
);
}
function Doctor({route}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Doctor Screen</Text>
<Text>{route?.params?.name}</Text>
</View>
);
}
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
<Button
title="Doctor"
onPress={() =>
navigation.navigate('Hospital', {
screen: 'Doctor',
params: { name: 'Doc 1' },
})
}
/>
</View>
);
}
function HospitalView() {
return (
<Stack.Navigator>
<Stack.Screen name="Doctor" component={Doctor} />
<Stack.Screen name="Patient" component={Patient} />
</Stack.Navigator>
);
}
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen
name="Hospital"
component={HospitalView}
options={({ navigation }) => ({
tabBarButton: (props) => (
<TouchableOpacity
{...props}
onPress={() =>
navigation.navigate('Hospital', { screen: 'Patient' })
}
/>
),
})}
/>
</Tab.Navigator>
);
}
You can refer this sample
https://snack.expo.io/#guruparan/5f3f1d
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
This is the error I am receiving. Please tell me what file you need to see. Apologies if the problem is not to the point, I am a noob here.
First image shows the error
Below is the file which calls for stack navigator.
This file calls for stacknavigator
If you need anything else, please tell me, I'll add it in further edits
import React from "react";
import { Easing, Animated, Dimensions } from "react-native";
//import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import { createDrawerNavigator,DrawerItems} from 'react-navigation-drawer';
//import { createBottomTabNavigator } from 'react-navigation-bottom-tabs';
import { Block } from "galio-framework";
// screens
import HomeActivity from '../components/HomeActivity.js';
import ProfileActivity from '../components/ProfileActivity.js';
import Results from '../components/Results.js';
// import HomeActivity from "../screens/Home";
// import Onboarding from "../screens/Onboarding";
import Pro from "../components/Pro";
import Profile from "../components/Profile";
// import Register from "../screens/Register";
// import Elements from "../screens/Elements";
// import Articles from "../screens/Articles";
// drawer
import CustomDrawerContent from "./Menu";
// header for screens
import Icon from "../components/Icon";
import Header from "../components/Header";
import { argonTheme, tabs } from "../constants";
const { width } = Dimensions.get("screen");
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
//const Tab = createBottomTabNavigator();
function ElementsStack(props) {
return (
<Stack.Navigator mode="card" headerMode="screen">
<Stack.Screen
name="Elements"
component={Profile}
options={{
header: ({ navigation, scene }) => (
<Header title="Elements" navigation={navigation} scene={scene} />
),
cardStyle: { backgroundColor: "#F8F9FE" }
}}
/>
<Stack.Screen
name="Pro"
component={Pro}
options={{
header: ({ navigation, scene }) => (
<Header
title=""
back
white
transparent
navigation={navigation}
scene={scene}
/>
),
headerTransparent: true
}}
/>
</Stack.Navigator>
);
}
function ArticlesStack(props) {
return (
<Stack.Navigator mode="card" headerMode="screen">
<Stack.Screen
name="Articles"
component={Articles}
options={{
header: ({ navigation, scene }) => (
<Header title="Articles" navigation={navigation} scene={scene} />
),
cardStyle: { backgroundColor: "#F8F9FE" }
}}
/>
<Stack.Screen
name="Pro"
component={Pro}
options={{
header: ({ navigation, scene }) => (
<Header
title=""
back
white
transparent
navigation={navigation}
scene={scene}
/>
),
headerTransparent: true
}}
/>
</Stack.Navigator>
);
}
function ProfileStack(props) {
return (
<Stack.Navigator initialRouteName="Profile" mode="card" headerMode="screen">
<Stack.Screen
name="Profile"
component={ProfileActivity}
options={{
header: ({ navigation, scene }) => (
<Header
transparent
white
title="Profile"
navigation={navigation}
scene={scene}
/>
),
cardStyle: { backgroundColor: "#FFFFFF" },
headerTransparent: true
}}
/>
<Stack.Screen
name="Pro"
component={Pro}
options={{
header: ({ navigation, scene }) => (
<Header
title=""
back
white
transparent
navigation={navigation}
scene={scene}
/>
),
headerTransparent: true
}}
/>
</Stack.Navigator>
);
}
function HomeStack(props) {
return (
<Stack.Navigator mode="card" headerMode="screen">
<Stack.Screen
name="Home"
component={HomeActivity}
options={{
header: ({ navigation, scene }) => (
<Header
title="Home"
search
options
navigation={navigation}
scene={scene}
/>
),
cardStyle: { backgroundColor: "#F8F9FE" }
}}
/>
<Stack.Screen
name="Pro"
component={Pro}
options={{
header: ({ navigation, scene }) => (
<Header
title=""
back
white
transparent
navigation={navigation}
scene={scene}
/>
),
headerTransparent: true
}}
/>
</Stack.Navigator>
);
}
export default function OnboardingStack(props) {
return (
<Stack.Navigator mode="card" headerMode="none">
<Stack.Screen
name="Jobs"
component={Results}
option={{
headerTransparent: true
}}
/>
<Stack.Screen name="App" component={AppStack} />
</Stack.Navigator>
);
}
function AppStack(props) {
return (
<Drawer.Navigator
style={{ flex: 1 }}
drawerContent={props => <CustomDrawerContent {...props} />}
drawerStyle={{
backgroundColor: "white",
width: width * 0.8
}}
drawerContentOptions={{
activeTintcolor: "white",
inactiveTintColor: "#000",
activeBackgroundColor: "transparent",
itemStyle: {
width: width * 0.75,
backgroundColor: "transparent",
paddingVertical: 16,
paddingHorizonal: 12,
justifyContent: "center",
alignContent: "center",
alignItems: "center",
overflow: "hidden"
},
labelStyle: {
fontSize: 18,
marginLeft: 12,
fontWeight: "normal"
}
}}
initialRouteName="Home"
>
<Drawer.Screen name="Home" component={HomeStack} />
<Drawer.Screen name="Profile" component={ProfileStack} />
<Drawer.Screen name="Results" component={OnboardingStack} />
<Drawer.Screen name="Elements" component={ElementsStack} />
{/* <Drawer.Screen name="Articles" component={ArticlesStack} /> */}
</Drawer.Navigator>
);
}
So based on the error i would say that validateRouteConfigMap.js is getting an object(routeConfigs) that you've not yet defined or passed in as undefined or you have not set a default value for. Remember Object.keys(ARRAY_OR_OBJECT_VARIABLE) requires a variable of object type (besides null)
Im trying to pass some props to though an screenstack element in react native. I have a button which onPress will ask for a screen using react navigation like this:
<Button
title="Lees Meer"
color="#d10a10"
onPress={() => RootNavigation.navigate('Article', {
params: { title: title, text: text, image: image },
})}
/>
I want thos params to be used in the article to fill in the text, title and image. I thought I could just use them like this in the article:
function ArticleFull({ navigation, params }) {
return (
<View>
<Header/>
<Card>
<CardItem header bordered>
<Body>
<Image
style={{ width: '100%', height: 400 }}
source={{ uri: 'https://www.holland.com/upload_mm/9/a/b/68638_fullimage_zwolle_sassenpoort.jpg' }}
/>
<Text>
{params.text}
</Text>
</Body>
</CardItem>
</Card>
<Button
title="Go Back"
color="#d10a10"
width= "10%"
onPress={() => navigation.goBack()}
/>
</View>
);
}
export default ArticleFull;
In the app.js i made these screenstacks which is used to navigate to an article but i need it to contain some params which are set in the homepage using the button.
const Stack = createStackNavigator();
App.js:
export default class App extends Component {
render() {
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName="Home" >
<Stack.Screen options={{headerShown: false}} name="Home" component={HomePage} />
<Stack.Screen options={{headerShown: false}} name="Article" component={ArticleFull} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
You can pass like this:-
<Button
title="Go to Details"
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
and receive like this:-
function DetailsScreen({ route, navigation }) {
/* 2. Get the param */
const { itemId } = route.params;
const { otherParam } = route.params;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>itemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
);
}
source
Hope it helps!!!
My stack navigator
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="PageA" component={PageA} options={{title:'Page=A'}} />
<Stack.Screen name="PageB" component={PageB} options={{title:'Page=B'}} />
<Stack.Screen name="Menu" component={MenuTabNavigator} options={{title:'Menu'}} />
</Stack.Navigator>
</NavigationContainer>
and my tab navigator
const MenuTabNavigator = () => {
return (
<Tab.Navigator>
<Tab.Screen name="PageA" component={PageA} />
<Tab.Screen name="PageB" component={PageB} />
<Tab.Screen name="Menu" component={Menu} />
</Tab.Navigator>
);};
I'm using Tab Navigator with Stack Navigator.
ScreenA, Screen B and Menu screen in my Tabs.
I pass MenuTabNavigator to StackNavigator's Menu Component as you can see.
Problem:
When I use tabs, header title stays 'Menu'.
For example when I touch to PageB on tab, i expect header title should be 'PageB' but it stays 'Menu'.
How can I change header title for screens when i use bottom tabs?
The approach you are using is wrong. if you go this way you have to create three StackNavigators so that you can get three different headers. and then wrap them in a tab navigator. but this is the wrong way to use it.
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
function HomeScreen({ navigation }) {
navigation.setOptions({ title: 'Home' })
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
function SettingsScreen({ navigation }) {
navigation.setOptions({ title: 'Setting' })
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
function Menu({ navigation }) {
navigation.setOptions({ title: 'Menu' })
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Menu</Text>
</View>
);
}
const StackHome = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
);
};
const StackSetting = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Setting" component={SettingsScreen} />
</Stack.Navigator>
);
};
const StackMenu = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Menu" component={Menu} />
</Stack.Navigator>
);
};
const Tab = createBottomTabNavigator();
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="PageA" component={StackHome} options={{ title: "Home" }} />
<Tab.Screen name="PageB" component={StackSetting} options={{ title: "Settings"
}}
/>
<Tab.Screen name="Menu" component={StackMenu} options={{ title: "Menu" }} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;