Functions are not valid as a React child (React-Native) - react-native

I can not wrap my head around and understand why am I given this warning, I am using two kinds of navigators ( tab navigator and stack navigator ), in App.js and I cannot seem to get them working correctly
App.js
export const Tabs = TabNavigator({
'FormScreen': {
screen: FormScreen,
navigationOptions: {
tabBarLabel: 'Contact Us',
tabBarIcon: ({ tintColor }) => <Icon name="ios-contact" type="entypo" size={28} color={tintColor} />
}
},
'ListScreen': {
screen: ListScreen,
navigationOptions: {
tabBarLabel: 'Contact List',
tabBarIcon: ({ tintColor }) => <Icon name="list" type="entypo" size={28} color={tintColor} />
}
}
});
export const NavigationScreen = () => {
return StackNavigator(
{
Tabs: {
screen: Tabs,
navigationOptions: {
gesturesEnabled: false
}
}
},
{
headerMode: "none",
mode: "modal"
}
);
};
export default class App extends React.Component {
render() {
return (
<NavigationScreen/>);
}
}

Related

How to properly stack nested navigators within an App Container?

I currently have an AppContainer which consists of a bottom tab navigator and currently one more navigation set for Profile settings. I am trying to understand how to properly arrange everything, specifically how to nest navigation in one tab Profile so I can access two other pages QrCode and EditAccount each through two buttons. How do I achieve the below output? I believe I have to nest profile navigation somehow.
MY TABS BELOW
Home
Queue
Profile
:in here i have two other pages i can access QRCode and EditAccounts
App.js
const bottomTabNavigator = createBottomTabNavigator(
{
Home: {
screen: Home,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="home" size={25} color={tintColor} />
)
}
},
Queue: {
screen: Queue,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="comments" size={25} color={tintColor} />
)
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="user" size={25} color={tintColor} />
)
}
}
})
const navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hello',
headerStyle: {
height: '45%',
backgroundColor: 'black'
},
headerTintColor: 'white',
headerLeft: (
<TouchableWithoutFeedback
style={{ /* Put your style here */}}
onPress={() => navigation.goBack()} >
<Ionicons name="ios-arrow-dropleft" size={32} color="white" />
</TouchableWithoutFeedback>
)
}
}
const ProfileNavigator = createStackNavigator({
//Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
Profile: { screen: Profile },
EditAccount: { screen: EditAccount, navigationOptions }
});
const AppNavigator = createSwitchNavigator({
tabs: bottomTabNavigator,
profile: ProfileNavigator
})
const AppContainer = createAppContainer(AppNavigator);
After Applying Answer
The solution offers works as needed with the code below:
const navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hello',
headerStyle: {
height: '45%',
backgroundColor: 'black'
},
headerTintColor: 'white',
headerLeft: (
<TouchableWithoutFeedback
style={{ /* Put your style here */}}
onPress={() => navigation.goBack()} >
<Ionicons name="ios-arrow-dropleft" size={32} color="white" />
</TouchableWithoutFeedback>
)
}
}
const ProfileNavigator = createStackNavigator({
Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
});
//ADDED
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};
const AppNavigator = createSwitchNavigator({
tabs: bottomTabNavigator,
profile: ProfileNavigator
})
const AppContainer = createAppContainer(AppNavigator);
The only issue is I am unsure how to port over the header options to the tabs in the in the bottomTabNavigator. I have a custom component in place for the profile page that makes it look like this (black bar with button icon for Profile):
I can then navigate to the EditAccounts by pressing on the user icon. But when I navigate back from EditAccounts back to profile, the page renders with the navigationOptions header like so:
How do I apply this properly so I can lean simply on the navigationOptions header and push a custom name onto it (get rid of my custom component in that case)?
There are 2 cases.
If you want to keep tabs when accessing QR or EditAccount
const BottomTabNavigator = createBottomTabNavigator({
...
Profile: {
screen: ProfileNavigator,
}
})
const ProfileNavigator = createStackNavigator({
Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
});
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};
const AppContainer = createAppContainer(BottomTabNavigator);
If you don't want to keep tabs
const BottomTabNavigator = createBottomTabNavigator({
...
Profile: {
screen: Profile,
}
})
const AppNavigator = createStackNavigator({
Tabs: BottomTabNavigator,
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
})
const AppContainer = createAppContainer(AppNavigator);
UPDATED:
Add navigationOptions to ProfileNavigator to remove header
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};

How to add drawerIcon to a drawer navigator that contains a nested stack navigator?

I have a stack navigator which is then nested inside a drawer navigator. I want to have an icon for each of the routes in the stack navigator to then show in the drawer menu.
I have tried to use the navigationOptions to provide each route with a drawer icon in the stack navigator but this did not work.
const StackNavigation = createStackNavigator(
{
Setting: {
screen: SettingScreen,
navigationOptions: {
drawerIcon: ({ tintColor }) => <Feather name="settings" style={{ fontSize: 24, color: tintColor }} />,
},
},
Home: {
screen: HomeScreen,
navigationOptions: {
drawerIcon: ({ tintColor }) => <Feather name="home" style={{ fontSize: 24, color: tintColor }} />,
},
},
},
{
initialRouteName: 'Home',
headerMode: Platform.OS === 'android' ? 'screen' : 'float',
defaultNavigationOptions: ({ navigation }) => ({
headerLeft: <MenuButton navigation={navigation} />,
}),
}
);
const MainNavigation = createDrawerNavigator(
{
Home: {
screen: StackNavigation,
},
},
{
contentComponent: CustomDrawerComponent,
}
);
No icons are applied when attempted this way. Possibly because stack navigator does not have a drawerIcon option? But then I how do I apply them to each route individually...
If you have two paths,
import Icon from 'react-native-vector-icons/FontAwesome';
...
const MainTab = createDrawerNavigator(
{
Home: {
screen: HomeScreen
},
Setting: {
screen: SettingScreen
}
},
{
defaultNavigationOptions: ({ navigation }) => ({
drawerIcon: ({ focused, horizontal, tintColor, image }) => {
const { routeName } = navigation.state;
let iconName;
if (routeName === "Home") {
iconName = "home";
} else if (routeName === "Setting") {
iconName = "rocket";
}
return (
<Icon
name={iconName}
size={horizontal ? 20 : 25}
/>
);
}
})
}

Dynamically change bottom navigation icon based on drawer navigation icon

I want to dynamically change the icon of the bottom tab bar icon to the icon of the drawer page i change to. When the drawer open and i select cards page i want the payment icon to change from money to cards icon. Is this possible to achieve ? thanks
bottom tab
const bottomtab = createBottomTabNavigator(
{
home: {
screen: home,
navigationOptions: {
tabBarIcon: ({ tintColor, focused }) => <Icon name="home" color={tintColor} focused={focused} size={30} />,
},
},
payment: {
screen: paymentdrawerstack,
navigationOptions: ({ navigation }) => {
return{
tabBarIcon: ({ tintColor }) => <Icon name="money" color={tintColor} size={20} />,
}
},
},
},
drawer nav
const paymentdrawerstack = createDrawerNavigator(
{
balance: {
screen: balance,
navigationOptions: ({ navigation }) => {
return{
drawerIcon: ({ tintColor, focused }) => (
<Icon name="money" color={tintColor} focused={focused} size={20} />
),
drawerLabel: 'balance',
}
},
},
cards: {
screen: cards,
navigationOptions: ({ navigation }) => {
return{
drawerIcon: ({ tintColor, focused }) => (
<Icon name="cards" color={tintColor} focused={focused} size={20} />
),
drawerLabel: 'cards',
}
},
},
Try this, use this function to get the active route:
const getActiveRoute = route => {
if (!route.routes || route.routes.length === 0 || route.index >= route.routes.length) {
return route.routeName;
}
const childActiveRoute = route.routes[route.index];
return getActiveRoute(childActiveRoute);
}
And then in navigationOptions of payment, check what the active route is, change the icon accordingly, like so:
payment: {
screen: paymentdrawerstack,
navigationOptions: ({ navigation }) => {
const name = getActiveRoute(navigation.state) === 'cards' ? 'cards' : 'money';
return {
tabBarIcon: ({ tintColor }) => <Icon name={name} color={tintColor} size={20} />,
}
},
},

React Navigation: Is it possible to navigate to the same screen from different stackNavigators?

I'm coding an app that uses react-navigation. My app has a bottom TabNavigator in which each tab consists of a stackNavigator, like so:
Routes.js:
const FirstStack = createStackNavigator({
First: {
screen: FirstScreen,
navigationOptions: {
header: props => <AppToolbar />
}
}
});
const SecondStack = createStackNavigator({
Second: {
screen: SecondScreen,
navigationOptions: {
header: props => <AppToolbar />
}
}
});
const ThirdStack = createStackNavigator({
Third: {
screen: ThirdScreen,
navigationOptions: {
header: props => <AppToolbar />
}
}
});
const FourthStack = createStackNavigator({
Fourth: {
screen: FourthScreen,
navigationOptions: {
header: props => <AppToolbar />
}
}
});
const FifthStack = createStackNavigator({
Fifth: {
screen: FifthScreen,
navigationOptions: {
header: props => <AppToolbar />
}
}
});
export const AppStack = createBottomTabNavigator(
{
First: {
screen: FirstStack,
navigationOptions: {
title: "First",
tabBarLabel: "First",
tabBarIcon: ({ tintColor }) => (
<Icon name="ios-home" color={tintColor} size={24} />
)
}
},
Second: {
screen: SecondStack,
navigationOptions: {
title: "Second",
tabBarLabel: "Second",
tabBarIcon: ({ tintColor }) => (
<Icon name="ios-clock" color={tintColor} size={24} />
)
}
},
Third: {
screen: ThirdStack,
navigationOptions: {
title: "Third",
tabBarLabel: "Third",
tabBarIcon: ({ tintColor }) => (
<Icon name="ios-fitness" color={tintColor} size={24} />
)
}
},
Fourth: {
screen: FourthStack,
navigationOptions: {
title: "Fourth",
tabBarLabel: "Fourth",
tabBarIcon: ({ tintColor }) => (
<Icon
name="ios-cloud-download"
color={tintColor}
size={24}
/>
)
}
},
Fifth: {
screen: FifthStack,
navigationOptions: {
title: "Fifth",
tabBarLabel: "Fifth",
tabBarIcon: ({ tintColor }) => (
<Icon name="ios-person" color={tintColor} size={24} />
)
}
}
},
{
initialRouteName: "First",
order: ["First", "Second", "Third", "Fourth", "Fifth"],
tabBarOptions: {
activeTintColor: "white",
inactiveTintColor: "grey",
style: {
backgroundColor: "#121212",
borderTopColor: "#303030"
}
}
}
);
Each stackNavigator inside the tabs in the tabNavigator has its own header so that I can customize the header for each tab, but all headers have a button which should navigate to the profile screen (in this example its the contact icon).
AppToolbar.js:
const appToolbar = props => {
return (
<View style={styles.toolbar}>
<Text style={styles.toolbarTitle}>Title</Text>
<TouchableOpacity onPress={...}>
<Icon
name="ios-contact"
color="grey"
size={30}
style={{ padding: 0, margin: 0, marginRight: 10 }}
/>
</TouchableOpacity>
</View>
);
};
What I want to do is that by pressing the contact icon the app should navigate to the profile screen, what I don't know is if its possible to define like a global route thats accesible from everywhere, Or do I have to add the profile screen to all the stackNavigators so that it is accesible from every screen in every stack?
Thanks in advance!
Found the answer here https://stackoverflow.com/a/50701940/1276438
Use a stackNavigator with the profile stack and the tabNavigator as children, making the tabNavigator the default route.

Force scrollview to re-render when closing a react-navigation modal?

I'm opening a modal, nested in a react-navigation TabNavigator.
This modal is used to add, say a teammate, to a Realm database.
When the new teammate is saved, I close the modal and "navigate" back to a ScrollView displaying all the teammates, retrieved from my Realm database.
My problem is that this ScrollView doesn't re-render when closing the modal.
I am not using Redux or any other state manager.
This is the code executed when saving a user:
onButtonPress() {
var teammate = new TeammateModel(this.firstname, this.lastname);
if (this.firstname && this.lastname) {
TeammateService.save(teammate);
this.props.navigation.goBack(null);
}
}
And this is the code of the for the component rendering the ScrollView:
export default class Team extends Component {
componentWillMount() {
teammates = TeammateService.findAll();
}
render() {
return (
<ScrollView>
<List>{teammates.map((teammate) => (
<ListItem
key={teammate.id}
title={`${teammate.firstname} ${teammate.lastname}`}
/>
))}
</List>
</ScrollView>
);
}
}
As requested here's also my routes.js where the navigation happens:
export const TeamStack = StackNavigator({
Team: {
screen: Team,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Team',
headerRight: <Button title="Add" onPress={() => navigation.navigate('EditTeammate')} />
}),
},
})
export const EditTeammateStack = StackNavigator({
EditTeammate: {
screen: EditTeammate,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Teammate',
headerLeft: <Button title="Cancel" onPress={() => navigation.goBack(null)} />
}),
}
})
export const NoteStack = StackNavigator({
EditNote: {
screen: EditNote,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Edit note'
})
}
})
export const Tabs = TabNavigator({
EditNote: {
screen: NoteStack,
navigationOptions: ({ navigation }) => ({
header: { visible: true },
headerTitle: 'Edit note',
tabBarLabel: 'Notes',
tabBarIcon: ({ tintColor }) => <Icon name="list" size={20} color={tintColor} />
}),
},
Team: {
screen: TeamStack,
navigationOptions: ({ navigation }) => ({
header: { visible: true },
headerTitle: 'Team',
tabBarLabel: 'Team',
tabBarIcon: ({ tintColor }) => <Icon name="users" size={20} color={tintColor} />
}),
}
}, {
initialRouteName: 'Team',
});
export const Root = StackNavigator({
Tabs: {
screen: Tabs,
},
EditTeammate: {
screen: EditTeammateStack,
},
}, {
mode: 'modal',
headerMode: 'none'
});
export default Root;
I'm sure I'm doing one (or more) thing(s) wrong. But what? :)