Styling Header with React Navigation(From V3 to V5) - react-native

So I'm following along with this tutorial trying to make an app with react native and it uses v3 of react-navigation but I'm trying to use v5. The issue is with this the styling that was previously working in the old version no longer does.
**// setup of navigator**
const Stack = createStackNavigator();
const Travel = () => {
return (
<Stack.Navigator initialRouteName="List">
<Stack.Screen name="Article" component={Article} />
<Stack.Screen
name="List"
component={List}
options={{
title: null,
}}
/>
</Stack.Navigator>
);
};
**// screen in question - List**
const styles = StyleSheet.create({
flex: {
flex: 1,
},
row: {
flexDirection: 'row',
},
header: {
backgroundColor: 'orange',
},
});
export default class List extends React.Component {
static navigationOptions = {
header: (
<View style={[styles.flex, styles.row, styles.header]}> **// styles won't get applied for some reason**
<View>
<Text>Search for a place</Text>
<Text>Destination</Text>
</View>
<View>
<Text>Avatars</Text>
</View>
</View>
),
};
render() {
return <Text>List</Text>;
}
}
I'm at a loss to make this work and look like this: goal navigation
Any idea how to update this code from this v3 version in the tutorial to the v5 support version of react-navigation?
UPDATE:
I've gotten closer to the goal with this code:
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { Text, View } from 'react-native';
import Article from '../screens/Article';
import { List } from '../screens/List';
const Stack = createStackNavigator();
const ListHead = () => {
return (
<View
style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: 'orange',
}}
>
<View>
<Text>Search for a place</Text>
<Text>Destination</Text>
</View>
<View>
<Text>Avatars</Text>
</View>
</View>
);
};
const Travel = () => {
return (
<Stack.Navigator initialRouteName="List">
<Stack.Screen name="Article" component={Article} />
<Stack.Screen
name="List"
component={List}
options={{
headerTitle: () => <ListHead />,
}}
/>
</Stack.Navigator>
);
};
export default Travel;
However I can't get these to properly be spaced apart like the goal photo and they don't take up the full width.

In react-navigation v5 you can use headerLeft and headerRight for a custom navigation header like this. You can also specify the styling according to your needs.
Here is the code example
export default class List extends React.Component {
static navigationOptions = {
headerLeft: () => (
<View>
<Text>Search for a place</Text>
<Text>Destination</Text>
</View>
),
headerRight: () => (
<View>
<Text>Avatars</Text>
</View>
),
// you can specify your styles here also
headerStyle: {},
headerRightContainerStyle: {},
headerLeftContainerStyle: {},
};
render() {
return <Text>List</Text>;
}
}
I hope this will help you out.!

Related

React Native - Show native header back icon

is there a way how to show native back icon (different for android nad iOS) in custom header? I have created custom header, but have no idea how to show them.
I'm using react-navigation version 6
My stack:
<Stack.Navigator screenOptions={
stackOptions({
header: (props: any) => <Header {...props}/>,
headerRight: () => HeaderActionIcon({
icon: <SvgSettings/>,
action: () => navigation.navigate('Settings')
})
})}>
My header component:
const Header = ({ ...props }: any) => {
const { options, navigation } = props
return (
<View style={styles.container}>
<Image
resizeMode='repeat'
source={require('../../assets/img/pattern.png')}
style={styles.image}
/>
<View style={styles.wrapper}>
{/* Back press icon */}
<View style={styles.leftItem}>
{/* Back icon should be here.. */}
</View>
{/* Stack title */}
<View style={styles.textWrapper}>
<Text style={[options.headerTitleStyle, styles.titleText]} numberOfLines={1}>
{ options.title }
</Text>
</View>
{/* Settings icon */}
<View style={styles.rightItem}>
{ options.headerRight() }
</View>
</View>
</View>
)}
Thanks for every answer
Here is a sample.
import * as React from 'react';
import { View, Text, Platform } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
function HomeScreen({ navigation }) {
React.useLayoutEffect(() => {
navigation.setOptions({
headerTitle: () =>
Platform.select({
android: <Text>{'Left android'}</Text>,
ios: <Text>{'Left ios'}</Text>,
}),
headerRight: () =>
Platform.select({
android: <Text>{'Right android'}</Text>,
ios: <Text>{'Right ios'}</Text>,
}),
});
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
import * as React from 'react';
import { View, Text, Platform } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
function HomeScreen({ navigation }) {
React.useLayoutEffect(() => {
navigation.setOptions({
headerTitle: () =>
Platform.select({
android: <Text>{'Left android'}</Text>,
ios: <Text>{'Left ios'}</Text>,
}),
headerRight: () =>
Platform.select({
android: <Text>{'Right android'}</Text>,
ios: <Text>{'Right ios'}</Text>,
}),
});
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;

Navigate to other StackNavigator screen when press button on navbar

I'm pretty new to react and this is my first app.
I have a stack navigator with 2 screens by now: MainMenu and Profile. While the user is in MainMenu, a button on top right corner is shown and I need to redirect the user to the Profile screen when this button is pressed. I need something like this.props.navigation.navigate('Profile') but this does not work, because this, props and navigation are not defined.
My thinks are that I cannot redirect to Profile from this stack navbar, cause Profile is still defined yet, but I don't know another way to do it.
// mainStack.js
import React from 'react';
import { View, Text, TouchableOpacity, Image } from 'react-native';
import { createStackNavigator } from '#react-navigation/stack';
import MainMenu from '../../screens/home/mainMenu';
import Profile from '../../containers/profileContainer';
import Icon from 'react-native-vector-icons/FontAwesome';
import { useSelector } from 'react-redux';
const MainStack = () => {
const Stack = createStackNavigator();
const isAdmin = (useSelector(state => state.auth.user.role) === 'admin');
function renderUserMenu() {
return (
<TouchableOpacity style={{ marginRight: 20 }} onPress={() => console.log("HERE I NEED TO REDIRECT TO THE SCREEN PROFILE") } >
<Icon style={{ color: 'white' }} name='user-circle-o' size={30} />
</TouchableOpacity>
)
}
function LogoTitle() {
return (
<Image
style={{ width: 150, height: 50 }}
source={require('../../assets/logo-with-slogan.png')}
/>
);
}
function renderConfigBtn(_isAdmin) {
if (!_isAdmin) {
return (
<TouchableOpacity style={{ marginRight: 10 }} onPress={() => console.log('Configuraciones')} >
<Icon style={{ color: 'white' }} name='cog' size={30} />
</TouchableOpacity>
)
}
}
return (
<Stack.Navigator>
<Stack.Screen
name="MainMenu"
component={MainMenu}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<View style={{ flexDirection: 'row' }}>
{renderConfigBtn(isAdmin)}
{renderUserMenu()}
</View>
),
headerStyle: { backgroundColor: '#0064c8' },
}}
/>
<Stack.Screen
name="Profile"
component={Profile}
options={{
headerStyle: { backgroundColor: '#0064c8' },
}}
/>
</Stack.Navigator>
)
}
export default MainStack;
Also, this stack is inside a navigation container as follows:
import React from 'react';
import { useSelector } from "react-redux";
import { NavigationContainer } from "#react-navigation/native";
import AuthStack from "./authStack";
import MainStack from "./mainStack";
const AppNavigator = props => {
const isAuth = useSelector(state => !!state.auth.access_token);
return (
<NavigationContainer>
{ !isAuth && <AuthStack/>}
{ isAuth && <MainStack/>}
</NavigationContainer>
);
};
export default AppNavigator;
I would appreciate any help.
You can access 'navigation' in options like below
options={({navigation})=>({
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<View style={{ flexDirection: 'row' }}>
{renderConfigBtn(isAdmin,navigation)}
{renderUserMenu(navigation)}
</View>
),
headerStyle: { backgroundColor: '#0064c8' },
})}
Basically you can pass a function as a prop to options and navigation will be passed to it as a parameter.
function renderUserMenu(navigation) {
return (
<TouchableOpacity style={{ marginRight: 20 }} onPress={() => navigation.navigate('YOUR SCREEN') } >
<Icon style={{ color: 'white' }} name='user-circle-o' size={30} />
</TouchableOpacity>
)
}
And you can change the renderUserMenu function like above so that it will do the navigation as required.
Use navigation options and then pass it to the function to navigate to profile:
<Stack.Screen
name="MainMenu"
component={MainMenu}
options={({ navigation }) => ({ ......
We simply can import the useNavigation hook from the react-navigation/native package and can implement navigation with the use of this hook without accessing the navigation props from the component.
For Ex.
First import the hook,
import { useNavigation } from '#react-navigation/native';
Use the hook to implement navigation as below in MainStack.js:
const navigation = useNavigation();
function renderUserMenu() {
return (
<TouchableOpacity style={{ marginRight: 20 }} onPress={() => navigation.navigate("Profile") } >
<Icon style={{ color: 'white' }} name='user-circle-o' size={30} />
</TouchableOpacity>
)
}

React Native: Deep linking doesn't work properly with StackNavigator

I am trying deeplinking in React-Native. The code works properly when the app is in the background. But once I remove the app from background and try to launch it using the link in safari. The app is launched with details screen. But I could not find previous (Home) screens in the Navigation Stack. Please find the code below:
/* eslint-disable react-native/no-inline-styles */
import 'react-native-gesture-handler';
import React from 'react';
import {TouchableOpacity, Text, View} from 'react-native';
import {useLinking, NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
const HomeScreen = ({navigation}) => {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>Home Screen</Text>
<TouchableOpacity
onPress={() => {
navigation.navigate('Details', {itemId: 40});
}}>
<Text>Go to Details</Text>
</TouchableOpacity>
</View>
);
};
const DetailScreen = ({route, navigation}) => {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text>Details Screen</Text>
<Text>Item Id: {route.params.itemId}</Text>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Text>Go Back</Text>
</TouchableOpacity>
</View>
);
};
const Stack = createStackNavigator();
const App = () => {
const ref = React.useRef();
const {getInitialState} = useLinking(ref, {
prefixes: ['deeplink://'],
config: {
initialRouteName: 'Home',
Home: 'Home',
Details: {
path: 'Details/:itemId',
parse: {
itemId: null,
},
},
},
getPathFromState(state, config) {
console.log(state);
},
});
const [isReady, setIsReady] = React.useState(false);
const [initialState, setInitialState] = React.useState();
React.useEffect(() => {
Promise.race([
getInitialState(),
new Promise((resolve) => setTimeout(resolve, 150)),
])
.catch((e) => {
console.error(e);
})
.then((state) => {
if (state !== undefined) {
setInitialState(state);
}
setIsReady(true);
});
}, [getInitialState]);
if (!isReady) {
return null;
}
return (
<NavigationContainer
fallback={<Text>Loading...</Text>}
initialState={initialState}
ref={ref}>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
Launched the app using "deeplink://Details/86" in Safari.
First, update to latest version of #react-navigation/native and then follow the linking docs: https://reactnavigation.org/docs/configuring-links/
Instead of useLinking, you can pass a linking prop to the NavigationContainer component. Then change your config to following:
const App = () => {
const linking = {
prefixes: ["deeplink://"],
config: {
initialRouteName: "Home",
screens: {
Home: {
path: "home",
},
Details: {
path: "details/:itemId"
}
}
}
};
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
Then you can open links like deeplink://home or deeplink://details/someid.

× TypeError: Cannot read property 'navigate' of undefined on React-Native expo react-navigation 5.xx

If somebody can help me.
I'm sure that I miss something, but I can't see it.I'm trying to do navigation between 3 components and I create it with the documentation from the react-navigation page, I try a lot of different ways to do it, but always receive the same answer, so if somebody can see the mistake I do please tell me.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Header from './app/components/header/header'
import Main from './app/components/main/main';
import Data from './app/components/data/data';
import Grafic from './app/components/grafic/grafic';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import ButtonImage from './app/components/buttonImag/buttonImg'
import 'react-native-gesture-handler';
const Stack = createStackNavigator();
function MainScreen({ navigation }) {
return (
<ButtonImage onPress={() => navigation.navigate('Main')} heightV={40} widthV={40} itemImage={require('./app/resourse/home.png')} heightI={33} widthI={33} ></ButtonImage>
);
};
function DataScreen({ navigation }) {
return (
<ButtonImage onPress={() => navigation.navigate('Data')} heightV={40} widthV={40} itemImage={require('./app/resourse/datalist.png')} heightI={33} widthI={33} ></ButtonImage>
);
};
function GraficScreen({ navigation }) {
return (
<ButtonImage onPress={() => navigation.navigate('Grafic')} heightV={40} widthV={40} itemImage={require('./app/resourse/grafic.png')} heightI={33} widthI={33} ></ButtonImage>
);
};
const Footer = ({ navigation }) => {
return (
<View style={styles.header}>
<View style={styles.buttonPos}>
<MainScreen />
<DataScreen />
<GraficScreen />
</View>
</View>
)
};
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Main" component={() => <Main />} />
<Stack.Screen name="Data" component={() => <Data />} />
<Stack.Screen name="Grafic" component={() => <Grafic />} />
</Stack.Navigator>
);
}
const App = () => {
return (
<View style={{ flex: 1 }}>
<Header />
<NavigationContainer>
<MyStack />
</NavigationContainer>
<Footer />
</View>
);
}
const styles = StyleSheet.create({
header: {
backgroundColor: "#00BFFF",
height: 55
},
buttonPos: {
flex: 1,
flexDirection: 'row',
alignItems: "center",
justifyContent: "space-between",
padding: 7
},
conection: {
flex: 1,
flexDirection: 'row',
alignItems: "center",
justifyContent: "space-between",
padding: 7
}
});
export default App
What you need to do that is to use a Tab Navigator.
react-navigation v5 has 3 ways to do so:
createBottomTabNavigator
createMaterialBottomTabNavigator (very easy to customize)
createMaterialTopTabNavigator (with tabBarPosition: 'bottom')
Also you can customize the tabs by reading the documentation.
I made you a basic example using this last option:
import { SafeAreaView, View, Text, StyleSheet } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs'
import { createStackNavigator } from '#react-navigation/stack'
const Header = () => <View style={styles.header}><Text>Header title</Text></View>
const Main = () => <View style={styles.component}><Text>Main component</Text></View>
const Data = () => <View style={styles.component}><Text>Data component</Text></View>
const Grafic = () => <View style={styles.component}><Text>Grafic component</Text></View>
const footerConfig = {
tabBarPosition: 'bottom',
}
const Tabs = createMaterialTopTabNavigator()
const MyFooter = () => (
<Tabs.Navigator {...footerConfig}>
<Tabs.Screen name="Main" component={Main} />
<Tabs.Screen name="Data" component={Data} />
<Tabs.Screen name="Grafic" component={Grafic} />
</Tabs.Navigator>
)
const stackConfig = {
headerMode: 'none',
}
const Stack = createStackNavigator()
const MyStack = () => (
<Stack.Navigator {...stackConfig}>
<Stack.Screen name="Tabs" component={MyFooter} />
</Stack.Navigator>
)
export default () => (
<SafeAreaView style={styles.main}>
<Header />
<NavigationContainer>
<MyStack />
</NavigationContainer>
</SafeAreaView>
)
const styles = StyleSheet.create({
main: {
flex: 1,
},
header: {
height: 64,
width: '100%',
justifyContent: 'center',
alignItems: 'center',
},
component: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
})
EDIT:
You can use useNavigation hook inside your components. If you want to use your current configuration.
https://reactnavigation.org/docs/use-navigation/useNavigation
Also in order to send navigation props to components this:
<Stack.Screen name="Main" component={() => <Main />} />
should become:
<Stack.Screen name="Main" component={Main} />
// or
<Stack.Screen name="Main" component={props => <Main {...props} />} />
Go to you Main, Data, Grafic components and add {navigation} as your function parameter:
function Main({navigation}){
// rest of your codes
}
export default Main

How to remove the back button in the header react native

I want to remove the button back, but leave the header.
My component is as follows. I want to leave the title, and I don’t need the back button.
import React from 'react';
import { View } from 'react-native';
export const TrucksScreen = () => {
return (
<View>
....
</View>
);
});
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
};
How can I remove the button back?
Using headerLeft: null will be deprecated in future versions.
Instead use a function like so :
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
headerLeft: () => {
return null;
},
};
Cheers !
set headerLeft: null in the navigation Options. and this will remove the back button from the head as I did in the last line of code.
import React from 'react';
import { View } from 'react-native';
export const TrucksScreen = () => {
return (
<View>
....
</View>
);
});
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
headerLeft: null,
};
I hope it will help. Ask for doubts
According to the docs you can replace the header back button with whatever you want by passing options param in stack navigator . Do find the working example : expo-snack:
import * as React from 'react';
import { View, Text, Button, Image } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
function LogoTitle() {
return (
<Image
style={{ width: 50, height: 50 }}
source={require('#expo/snack-static/react-native-logo.png')}
/>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#00cc00"
/>
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Hopeit helps. feel free for doubts
import React from 'react';
import { View, TouchableOpacity, Image, Text } from 'react-native';
import PropTypes from 'prop-types';
import style from '../../../utils/style';
import images from '../../../images/images';
class Header extends React.Component {
constructor(props) {
super(props);
}
onRightIconPress = () => {
if (this.props.onRightIconPress) {
this.props.onRightIconPress();
}
};
render() {
const { title, navigation, showBackIcon, showRightIcon, rightIcon } = this.props;
return (
<View style={style.headerrowcontainer}>
{/* Back Button*/}
{showBackIcon ? (
<TouchableOpacity onPress={() => navigation.goBack()}>
<Image resizeMode="contain" source={images.iconback} style={style.backimage} />
</TouchableOpacity>
) : (
<View />
)}
{/* Title */}
<Text style={style.titleheader}>{title}</Text>
{/* Right Icon */}
{showRightIcon ? (
<Image name={rightIcon} style={style.rightIcon} onPress={this.onRightIconPress} />
) : (
<View />
)}
</View>
);
}
}
Header.defaultProps = {
title: '',
};
Header.propTypes = {
title: PropTypes.string,
};
export default Header;
Home: {
screen: HomeScreen,
navigationOptions: {
headerLeft: null,
}
}
try setting headerLeft: null
Doesn't work in RN6.
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
headerLeft: () => {
return null;
},
};