Adding Custom header in react-navigation 5 - reactjs

I know how to implement header in navigation 3, but I'm having trouble passing the navigation to my header component in version 5.
(HomeStack.js)
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { Button } from 'react-native';
import Home from '../screens/home';
import ReviewDetails from '../screens/reviewDetails';
import Header from '../shared/header';
const Stack = createStackNavigator()
export default Navigator = () => {
return (
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}
>
<Stack.Screen
name='Home'
component={Home}
options={{
headerTitle: ({navigation}) => <Header navigation={navigation}/>
}}
/>
<Stack.Screen name='ReviewDetails' component={ReviewDetails} />
</Stack.Navigator>
)
}
(header.js)
import React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { MaterialIcons } from '#expo/vector-icons';
export default function Header({ navigation }) {
const openMenu = () => {
navigation.openDrawer();
}
return (
<View style={styles.header}>
<MaterialIcons name='menu' size={28} onPress={openMenu} style={styles.icon}/>
<View>
<Text style={styles.headerText}>GameZone</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
header: {
width: '100%',
height: '100%',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center'
},
headerText: {
fontWeight: 'bold',
fontSize: 20,
color: '#333',
letterSpacing: 1,
},
icon: {
position: 'absolute',
left: 16
}
});
I think the only problem is how to pass the navigation var. to the header component in order to open and close the drawer. I am struggling with this problem. Your help is appreciated.

Related

Expo app crashing on android on rendering components

When i render a screen in App.js the app crashes on android but works fine on android. If i render some function defined in App.js in renders on screen.
App.js
import React from 'react';
import {enableScreens} from 'react-native-screens';
enableScreens();
import { ActivityIndicator, View, Text, Button, TextInput, StyleSheet, TouchableOpacity } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import {createNativeStackNavigator} from "#react-navigation/native-stack";
import Main from "./screens/Main"
function HomeScreen({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<TouchableOpacity onPress={() => {navigation.navigate("LoginPage")}}>
<Text>NEXT</Text>
</TouchableOpacity>
</View>
);
}
function S1({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<TouchableOpacity onPress={() => {navigation.navigate("main")}}>
<Text>NEXT</Text>
</TouchableOpacity>
</View>
);
}
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" options={{headerShown : false}} component={HomeScreen} />
<Stack.Screen name="LoginPage" options={{headerShown : false}} component={S1} />
<Stack.Screen name="main" options={{headerShown : false}} component={Main} />
</Stack.Navigator>
</NavigationContainer>
);
}
Main.js
import react, {useState} from "react";
import { View, Text, Button, TextInput, StyleSheet, TouchableOpacity } from "react-native";
export default function Main({navigation}){
const options = [
{
name: "Games",
redirect: "Games"
},
{
name : "AR Tutorials",
redirect: "AR"
},
{
name : "Competition",
redirect : "Competition"
}
];
return(
<View style={styles.container}
>
{
options.map(op => (
<TouchableOpacity
style={styles.card}key={op.name} onPress={() => {navigation.navigate(op.redirect)}}>
<Text style={styles.cardText}>{op.name}</Text>
</TouchableOpacity>
))
}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
card: {
backgroundColor: "rgb(0, 100, 255)",
minHeight: "25%",
minWidth: "70%",
borderRadius: 1,
marginVertical: "1rem",
display: "flex",
justifyContent: "flex-end",
alignItems: "center"
},
cardText : {
marginBottom: "0.5rem"
}
});
Here functions HomeScreen and S1 renders but on navigating to Main the app crashes whiteout any logs.
I have tried building apk ind then running but it shows same behaviour.

React Native Navigation (ver4.x) unable to get props in custom header component. Keep getting undefined error

I have a custom header in my app and I am trying to get the navigation to open the Drawer when clicked on the menu item in the header component. I've passed the navigation and the header text as props to the component. However the props are being returned as undefined.
This is my header
import React from 'react';
import {View, StyleSheet, Text, Image, Alert, TouchableOpacity } from 'react-native';
import Colors from '../constants/Colors';
import { MaterialIcons } from '#expo/vector-icons';
const Header = ({navigation, headerText}) => {
const openMenu = () => {
console.log({headerText}) **/// this prints Object { "headerText": undefined,}**
navigation.openDrawer() **/// this also throws an undefined error**
}
console.log(headerText)**/// this displays the headerText correct on loading**
return (
<View style={styles.header} >
<MaterialIcons onPress={openMenu} name='menu' size={30} style={styles.icon} />
<Text style={styles.logo}>My Home Page Header</Text>
</View>
);
};
const styles = StyleSheet.create({
header: {
paddingTop: 20,
width: '100%',
height: '10%',
flexDirection: 'row',
},
logo: {
height: '100%',
width: '90%',
fontFamily: 'pacifico-regular',
fontSize: 28,
paddingTop: 20,
paddingLeft: 20,
},
icon: {
marginTop:30,
paddingLeft: 10,
},
});
export default Header;
console.log(headerText)/// this displays the header text correctly when loading the component.
However trying to use the props in the Text or the View throws an undefined error.
Clicking on the MaterialIcon menu icon gives the error :
Object {
"headerText": undefined,
}
and
TypeError: undefined is not an object (evaluating 'navigation.openDrawer')
This is the homestack.js
import { createStackNavigator } from 'react-navigation-stack';
import NewFeaturedRecipes from '../screens/NewFeaturedRecipes';
import NewRecipeDetails from '../screens/NewRecipeDetails';
import Header from '../shared/Header';
import React from 'react';
import {navigation} from 'react-navigation'
// const navigation = navigation;
const screens = {
NewFeaturedRecipes: {
screen: NewFeaturedRecipes,
navigationOptions: ({navigation}) => {
return {
headerTitle: () => <Header navigation={navigation}
headerText='Testting headertext from hoomestack' />
}
}
},
NewRecipeDetails: {
screen: NewRecipeDetails,
navigationOptions: {
title: 'Recipe Details',
}
},
}
const HomeStack = createStackNavigator(screens, {
defaultNavigationOptions: {
headerStyle: {
height: 20,
},
}
});
export default (HomeStack);
Here the answer if anybody is interested. I've used React Navigation 5.x for the nested navigators and I am able to call the openDrawer() from the menu in the Header component.
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View, Button, DrawerLayoutAndroid } from 'react-native';
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import {createDrawerNavigator} from '#react-navigation/drawer';
import { MaterialIcons } from '#expo/vector-icons';
const Header = ({ headerText }) => {
const navigation = useNavigation()
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', flexDirection: 'row' }}>
<MaterialIcons onPress={()=> navigation.openDrawer()} name='menu' size={30} style={styles.icon} />
<Text>Header Text = {headerText}</Text>
</View>
)
}
const HomeScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button onPress={() => navigation.navigate('Details')} title='Goto Details Screen' />
</View>
)
}
const DetailScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Detail Screen</Text>
<Button onPress={() => navigation.navigate('Home')} title='Goto Home Screen' />
</View>
)
}
const HomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name='Home'
component={HomeScreen}
options={{
headerTitle: props => <Header headerText='Text from App' />
}}
/>
<Stack.Screen name='Details' component={DetailScreen} options={{
headerTitle: props => <Header headerText='Text from Details' />
}}
/>
</Stack.Navigator>
)
}
function NotificationsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', flexDirection:'column' }}>
<Text>Notifications Screen</Text>
<Button onPress={() => navigation.goBack()} title="Go back home" />
</View>
);
}
const Stack = createStackNavigator()
const Drawer = createDrawerNavigator()
const App = () => {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName='Home'>
<Drawer.Screen name='Home' component={HomeStack}/>
<Drawer.Screen name='Notifications' component={NotificationsScreen}/>
</Drawer.Navigator>
</NavigationContainer>
)
}
export default App
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

React Navigation Shared Element 5 Nested Navigators

I'm running into a little issue with react-navigation-shared-element 5, so I made a very basic app to show an example. Basically I have a tab navigator nested in a stack navigator and when navigating to the tab navigator I'd like for a shared element transition. This is what I have so far. The transition was working when I had both MainScreen and OtherScreen in the Stack Navigator, but when I moved OtherScreen to the tab navigator it stopped.
import {NavigationContainer} from '#react-navigation/native';
import MainScreen from './src/MainScreen';
import OtherScreen from './src/OtherScreen';
import OtherScreenSecond from './src/OtherScreenSecond';
import {createSharedElementStackNavigator} from 'react-navigation-shared-element';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Stack = createSharedElementStackNavigator();
const Tab = createBottomTabNavigator();
const TabStuff = () => {
return (
<Tab.Navigator>
<Tab.Screen name="OtherScreen" component={OtherScreen} />
<Tab.Screen name="OtherScreenSecond" component={OtherScreenSecond} />
</Tab.Navigator>
);
};
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator mode="modal" headerMode="none">
<Stack.Screen name="MainScreen" component={MainScreen} />
<Stack.Screen name="Tabs" component={TabStuff} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
import {SharedElement} from 'react-navigation-shared-element';
const MainScreen = (props) => {
return (
<View style={{...styles.container}}>
<SharedElement id="test">
<View
style={{
width: 100,
height: 100,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 50,
backgroundColor: 'orange',
marginBottom: 50,
}}></View>
</SharedElement>
<TouchableOpacity onPress={() => props.navigation.navigate('Tabs')}>
<Text>Next</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'red',
},
});
export default MainScreen;
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
import {SharedElement} from 'react-navigation-shared-element';
const OtherScreen = (props) => {
return (
<View style={{...styles.container}}>
<SharedElement id="test">
<View
style={{
width: 150,
height: 150,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 75,
backgroundColor: 'blue',
marginBottom: 50,
}}></View>
</SharedElement>
<TouchableOpacity onPress={() => props.navigation.pop()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'yellow',
},
});
OtherScreen.sharedElements = (route, otherRoute, showing) => {
return ['test'];
};
export default OtherScreen;
Any pointers would be greatly appreciated, thanks!
You have to add sharedElementsConfig to Stack. Screen specifying the id of the shared element to work while navigating back
Change code to:
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator mode="modal" headerMode="none">
<Stack.Screen
name="MainScreen"
sharedElementsConfig={(props) => [
{
id: 'test', animation: 'fade'
}
]}
component={MainScreen} />
<Stack.Screen name="Tabs" component={TabStuff} />
</Stack.Navigator>
</NavigationContainer>
);
};

how to make initialParams updated

I want to update initialParams of react native navigation on clicking of the menu item
import React, { useState } from 'react';
import {createStackNavigator} from '#react-navigation/stack'
import { NavigationContainer } from '#react-navigation/native';
import userMain from './../../components/users/userMain';
import { View, Icon } from 'native-base';
export const UserStack = () => {
const Stack = new createStackNavigator()
const [toggleSearch, setToggleSearch] = useState(true)
const changeStyleToggle = () => {
setToggleSearch( !toggleSearch )
// console.log('toggle search here ==== ', toggleSearch)
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="User Homepage"
component={userMain}
initialParams={{ toggleSearch: toggleSearch}}
options = {{
title: 'My home',
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => (
<View style={{flexDirection:'row', justifyContent:'space-evenly', width:120}}>
<Icon name='home' onPress={() => changeStyleToggle()} />
<Icon name='home' />
<Icon name='home' />
</View>
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
the component i am calling is userMain where i am calling initialParams value and on behalf of that i want to change style
import React from 'react'
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
import { Input } from 'native-base';
const userMain = (props) => {
const {toggleSearch} = props.route.params;
console.log(toggleSearch)
const check = toggleSearch == true ? 'true!!' : 'false!!'
return (
<View style={styles.container}>
<Input
placeholder="search"
style={styles.searchInput}
/>
<Text>user homepage here</Text>
<Text>{check}</Text>
<TouchableOpacity style={styles.btn}>
<Text style={styles.btnText}> + </Text>
</TouchableOpacity>
</View>
)
}
const styles = StyleSheet.create({
container: {
alignItems: 'center', flex: 1, justifyContent: 'center',
},
btn: {
position: 'absolute', justifyContent: 'center', alignItems: 'center',
bottom:10, right:10, width:60, height: 60,
backgroundColor: '#fff', borderRadius: 50, borderColor: '#000',borderWidth:1,
},
btnText: {
fontSize: 50, color: 'black',
},
searchInput: {
position:'absolute', top: 0, borderWidth: 1, width: 300, opacity:0
}
})
export default userMain
i have checked on clicking the icon state which is toggleSearch is updating but it is not updating the initialParams hence it is not working on userMain component as well .

React Native undefined is not an object (evaluating 'HomeScreen.props.navigation')

I'm trying to navigate to another screen on the react navigation header when pressing the left element icon.
Error: React native Undefined is not an object(evaluating'HomeScreen.props.navigation')
Here's a picture of the error
Using:
expo, react navigation v3 and User Avatar to display user's profile picture (this will redirect to the user's profile)
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, TouchableOpacity, Image, SafeAreaView, FlatList} from 'react-native';
import CardStack, { Card } from 'react-native-card-stack-swiper';
import UserAvatar from 'react-native-user-avatar';
import GradientButton from 'react-native-gradient-buttons';
import { createStackNavigator, createAppContainer, createDrawerNavigator } from 'react-navigation';
import Drawer from 'react-native-drawer';
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerLeft: (
<TouchableOpacity onPress={() => this.props.navigation.navigate('Profile')}>
<UserAvatar style={{marginLeft: 15}} size="28" name="User Test" color="#000"/>
</TouchableOpacity>
),
headerTitle: (
<View style={{flex:1, flexDirection:'row', justifyContent:'center', marginRight: 5}}>
<Image resizeMode="cover" style={{width:98, height:25}} source={require('./src/images/logo.png')}/>
</View>
),
headerRight:(
<TouchableOpacity>
<Image style={{width:28, height:28, marginRight: 15}} source={require('./src/images/share.png')}/>
</TouchableOpacity>
),
};
};
render() {
return (
<View style={styles.container}>
<View style={styles.content}>
<View style={styles.cardContainer}>
<Card style={styles.card}><Text style={styles.label} onPress={() => this.props.navigation.navigate('Feed')}>A</Text></Card>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Feed')}>
<Image style={{width:220, height:55}} source={require('./src/images/button.png')} onPress={() => this.props.navigation.navigate('Feed')}/>
</TouchableOpacity>
</View>
</View>
</View>
);
}
}
export default HomeScreen;
const drawerStyles = {
drawer: {
flex: 1.0,
backgroundColor: '#2E2E2E'
},
main: {
flex: 1.0,
backgroundColor: 'white'
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: '#EFF2F5'
},
buttonContainer:{
flex: 1,
flexDirection:'row',
justifyContent:'center',
position: 'absolute',
alignSelf: 'center',
marginTop: 30,
},
cardContainer:{
flexDirection:'row',
justifyContent:'center',
marginTop: 55,
},
card:{
width: 312,
height: 476,
backgroundColor: '#fff',
borderRadius: 25,
shadowColor: 'rgba(0,0,0,0.9)',
shadowOffset: {
width: 50,
height: 5
},
shadowOpacity:0.5,
},
label: {
lineHeight: 400,
textAlign: 'center',
fontSize: 55,
fontFamily: 'System',
color: '#000',
backgroundColor: 'transparent',
},
});
Here's the app.js file
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, TouchableOpacity, Image, SaveAreaView, ScrollView} from 'react-native';
import { createStackNavigator, createAppContainer, createDrawerNavigator } from "react-navigation";
import HomeScreen from './layout_home.js';
import FeedScreen from './layout_feed.js';
import ProfileScreen from './layout_profile.js';
const AppStackNavigator = createStackNavigator({
Home: {
screen: HomeScreen
},
Feed: {
screen: FeedScreen
},
Profile: {
screen: ProfileScreen
},
})
export default createAppContainer(AppStackNavigator);
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: '#808080'
},
});
You have already passed the navigation parameters.
You have to change it as the code below.
return {
headerLeft: (
<TouchableOpacity onPress={() => navigation.navigate('Profile')}>
<UserAvatar style={{marginLeft: 15}} size="28" name="User Test" color="#000"/>
</TouchableOpacity>
),

Resources