Set Screen Title in ReactNative createBottomTabNavigator - react-native

I am trying to set the Screen Title of the Navigated Screen by using navigator. But it's not working/ changing and doesn't throw an exception either.
Scenario
I want to load some states from Redux and display nice Header Title in the Navigation Screen when the user clicks on the bottom Tab.
User clicks on second Tab in bottom navigation tab
Load state from Redux
Set Header Title
Expected
I want to set the following text to the Screen title
headerTitle set in Screen
Actual
I declared my BottomTabNavigator as following:
import FilterScreen from '../screens/FilterScreen';
import ItemsListScreen2 from '../screens/ItemsListScreen copy';
const BottomTab = createBottomTabNavigator();
const INITIAL_ROUTE_NAME = 'List';
export default function BottomTabNavigator({ navigation, route }) {
// this code works
navigation.setOptions({ headerTitle: 'header Set in Navigator' });
const [isFilterVisible, toggleFilter] = useState(false);
return (
<>
<BottomTab.Navigator
initialRouteName={INITIAL_ROUTE_NAME}
tabBarOptions={{
inactiveBackgroundColor: '#42a5f5',
inactiveTintColor: '#ffffff'
}}
>
<BottomTab.Screen
name="List"
component={ItemsListScreen}
options={{
title: 'List',
tabBarIcon: ({ focused }) => <TabBarIcon focused={focused} name="md-calendar" iconType="ion" />,
}}
/>
<BottomTab.Screen
name="List2"
headerMode="screen"
component={ItemsListScreen2}
screen
options={{
title: 'title List2',
headerTitle: 'my header title',
tabBarIcon: ({ focused }) => <TabBarIcon focused={focused} name="md-calendar" iconType="ion" />,
}}
/>
....
</BottomTab.Navigator>
</>
);
}
In the above component, I could set the Screen Title by using navigation.setOptions and it works.
So, I tried to do the same operation to set the headerTitle in List2 Screen. But it doesn't have any effect to the screen header. It only changes the label of the tab to "this is a title".
import { StyleSheet, View, Text } from 'react-native';
const ItemsListScreen2 = ({ navigation }) => {
navigation.setOptions({
headerTitle: `headerTitle set in Screen`, // <-- no effect at the screen level
title: "this is a title"
});
return (
<View>
<Text>Screen 2</Text>
</View>
);
}
I tried to do console.log to navigation object passed to the component and I can see it here:
Could you please help me to sort out this issue and suggest me how I could set the title from the screen?
Do I need to create a new function (Eg. getHeaderTitle), load the Redux state and set the title due to the route within BottomTabNavigation component? I want to keep that component clean and I want to load the Redux States only when the user navigates to the screen. I'd like to keep the screen specific codes in its own related screen.

Related

How do I pass certain route parameters from a screen function to a navigation header button in React Native

I am new to react native and am basically wondering how to pass information from one screen to another if the button that triggers the shift from one screen to another is in the navigation class.
For example, If I have a screen:
export default function Screen() {
const data = ["a", "b", "c"]
}
and another screen with a SectionList section that requires that information:
const data = [
{
title: "",
data: {route.params?.data},
},
and finally within the navigation tab a custom navbar header:
<Stack.Screen
name="Screen"
component={ChatScreen}
options={({ route }) => ({
headerRight: () => (
<Pressable onPress={() => navigation.navigate("Screen", {data: *get data from
Screen here*)}>
<Ionicons
name="ellipsis-horizontal"
size={25}
color="#5C6AEF"
style={{ marginBottom: 2 }}
/>
</Pressable>
),
})}
/>
basically, how do I call the information from Screen in the Navigation tab to pass to the SectionList screen?
Thanks for the help!

How to update navigation header from screen?

react-native version 0.62
I am making a quiz app and on every new question(a new quiz screen, QuestionScreen.js), I want to update a label in the navigation bar which is declared in App.js
In App.js, the problem here is I don't know how to pass the state questionIndex to HomeScreen or QuestionScreen, normally I thought this would be using props like <Question questionIndex={questionIndex}>. I tried to pass them in options but that doesn't seem to work...
const App = () => {
const [questionIndex, setQuestionIndex] = useState(0);
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='Home'
>
<Stack.Screen
name='Home'
component={HomeScreen}
options={{
title:''
}}
/>
<Stack.Screen
name='QuestionScreen'
component={QuestionScreen}
options={({ navigation }) => ({
headerBackTitleVisible: false,
headerRight: ()=>(<HeaderRight pageIndex={questionIndex}/>),
headerRightContainerStyle: {
alignItems: "flex-end",
paddingRight: 25
},
})}
/>
function HeaderRight(props) {
return (
<Text>{props.pageIndex}/{questions.length-1}</Text>
);
}
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container} >
<ScrollView>
<TouchableOpacity onPress={()=>
//here I'm unable to retrieve the questionIndex props.
//tried navigation.getParams("questionIndex") or route.params
navigation.navigate('QuestionScreen', {
pageIndex: 0
})
}>
In QuestionScreen.js
function QuestionScreen({route, navigation, options}) {
const { pageIndex } = route.params;
[Full code here on github]https://github.com/liaoxsong/expofirst
I am a newbie to react-native and I come from native mobile development, this Stack.Navigator api looks weird, can't I just manage the navigation header from the screen component itself? That would make things easier.
it is difficult to customize the default header given by react navigation, what you can do is disabling the header and creating your own custom header.
disable header by giving prop headerMode={"none"} in createStackNavigator and have your own custom header on top,
to disable header docs : https://reactnavigation.org/docs/stack-navigator/#headermode

React Native navigating between two screens back & forth

I have Home screen where we search for an ItemCode, after we search ItemCode it will get details from API & screen will navigate to Screen 1(Item Details Screen) & from Screen 1 we have a search for ItemNutrients and after search it will get Nutrients & Navigate to Screen 2 (Nutrients Screen)
In other words
Home -> Screen 1 (Item Details Screen) -> Screen 2 (Nutrients Screen)
After getting the necessary details from API from search for Item Details & Nutrients, User can navigate between Item Details & Nutrients back and forth..
I could navigate from Screen 1 (item Details) to Screen2 (Nutrients Screen) and swipe back to Screen 1 (item Details) but how could I swipe forward to look at Screen 2 (Nutrients Screen) again without searching for nutrients in Screen 1 as I already have the data from API for Nutrients.
Any ideas on how to implement this ? this is basically navigate to Screen 1 to Screen 2 on search from Screen 1 or if Screen 2 search is already done and we have data so swipe forward should navigate to Screen 2, I can have a button to navigate to Screen 2, the button on Screen 1 conditinally appears only when we have API data for Nutrients Screen and it will navigate to Screen 2 on that button click, but i need swipe functionality as well to navigate forward
I have Home, Screen 1, Screen 2 in stackNavigator, is it alright or i could use any other navigators to achieve this.
I have researched how to do this and can't find it in the docs anywhere. Please let me know if I am missing it somewhere. Thanks!
Nutrition Context:
import React, { createContext, useState, useEffect } from "react";
export const NutriSyncContext = createContext();
const DEFAULT_IS_NUTRI_SCANNED = false;
export const NutriSyncContextProvider = ({ children }) => {
const [isNutritionScanned, setisNutritionScanned] = useState(
DEFAULT_IS_NUTRI_SCANNED
);
// When we first mount the app we want to get the "latest" conversion data
useEffect(() => {
setisNutritionScanned(DEFAULT_IS_NUTRI_SCANNED);
}, []);
const nutrionScanClick = ({ navigation }) => {
setisNutritionScanned(true);
//navigation.navigate("NutritionFacts");
};
const contextValue = {
isNutritionScanned,
setisNutritionScanned,
nutrionScanClick,
};
return (
<NutriSyncContext.Provider value={contextValue}>
{children}
</NutriSyncContext.Provider>
);
};
inside navigation.js
const tabPNMS = createMaterialTopTabNavigator();
function tabPNMSStackScreen() {
const { isNutritionScanned } = useContext(NutriSyncContext);
console.log(isNutritionScanned);
return (
<tabPNMS.Navigator initialRouteName="PNMSNutrition" tabBar={() => null}>
<tabPNMS.Screen name="PNMSNutrition" component={PNMSNutrition} />
{isNutritionScanned ? (
<tabPNMS.Screen name="NutritionFacts" component={NutritionFacts} />
) : null}
</tabPNMS.Navigator>
);
}
and In stack Navigation:
const SearchStack = createStackNavigator();
const SearchStackScreen = () => (
<NutriSyncContextProvider>
<SearchStack.Navigator
initialRouteName="Search"
screenOptions={{
gestureEnabled: false,
headerStyle: {
backgroundColor: "#101010",
},
headerTitleStyle: {
fontWeight: "bold",
},
headerTintColor: "#ffd700",
headerBackTitleVisible: false, //this will hide header back title
}}
headerMode="float"
>
<SearchStack.Screen
name="Search"
component={Search}
options={({ navigation }) => ({
title: "Search",
headerTitleAlign: "center",
headerLeft: () => (
<Entypo
name="menu"
size={24}
color="green"
onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}
/>
),
})}
/>
<SearchStack.Screen
name="NutriTabs"
options={() => ({
title: "PNMS",
headerTitleAlign: "center",
})}
component={tabPNMSStackScreen}
/>
</SearchStack.Navigator>
</NutriSyncContextProvider>
);
I have NutriSyncContextProvider around the stack, but when I click the button on my page it will call nutrionScanClick method in context class it works just fine but when i do navigation.navigate to the NutritionFacts tab that is enabled i get error saying NutritionFacts tab doesn't exist
I got it working below is the code:
const nutrionScanClick = () => {
if (!isNutritionScanned) setisNutritionScanned(true);
else {
//Make a API call here for new data for the new scanned one's and then navigate
navigation.navigate("NutritionFacts");
}
};
useEffect(() => {
if (isNutritionScanned) {
navigation.navigate("NutritionFacts");
}
}, [isNutritionScanned]);
There's no swipe back n forth between screens in a stack navigator. You need to use a tab navigator to be able to keep multiple screens like that.
https://reactnavigation.org/docs/material-top-tab-navigator
import { createMaterialTopTabNavigator } from '#react-navigation/material-top-tabs';
const Tab = createMaterialTopTabNavigator();
function MyTabs() {
return (
<Tab.Navigator tabBar={() => null}>
<Tab.Screen name="ItemDetails" component={ItemDetails} />
{showNutrients ? <Tab.Screen name="Nutrients" component={Nutrients} /> : null}
</Tab.Navigator>
);
}

React Native StackNavigator: Passing props from a screen to the header of the next screen

I have a React Native StackNavigator like so:
const AppStack = () => {
return (
<NavigationContainer theme={{ colors: { background: "white" }}}>
<Stack.Navigator headerMode="screen">
<Stack.Screen name="Master" component={ Master } />
<Stack.Screen
name="Details"
component={ Details }
options={{ headerTitle: props => <Header {...props} /> }} // <-- how can I pass props from Master to the Header here
/>
</Stack.Navigator>
</NavigationContainer>
)
}
The navigation works fine. When I'm on Master, if I press a TouchableOpacity, it brings up Details, with the header component Header.
However, what I want is to pass props from Master to the Header component in Details.
Something like this:
{/* What I want is that onPress, I want to pass someones_name and someones_photo_url to
the Details' Header component */ }
const Master = () => {
const someones_name = "Steve";
const someones_photo_url = "http://somephoto.com/001.jpg";
return (
<TouchableOpacity onPress={() => navigation.navigate("Details")}>
<Text>{ someones_name }</Text>
<Image source={{ uri: someones_photo_url }}>
</TouchableOpacity>
)
}
Is this possible?
You can pass values to other screens when navigating by doing the following:
Assume I am on screen "Feed" and click on someones name thus I want to go to screen "Profile" where it will show that user name that I clicked on.
In screen Feed
props.navigation.navigate('Profile, { userName: 'Shane' })`
In screen Profile I can grab that value passed in via navigate with:
const { userName } = props.route.params
To set this up inside a header assuming your screen options are outside the screen component and cannot access the props. Look into using setOptions to configure your header title dynamically.
props.navigation.setOptions({
headerTitle: ...
})

React Navigation (V2 / V3): How to access navigation.state.index in navigationOptions on screen

I'm building a React Native app and for my navigation I use React Navigation (V3). For my UI elements I use React Native Elements.
I have a screen which I reuse in multiple navigators. Sometimes it is at the root of a stack, and sometimes it's pushed on with a stack. I want to programmatically add a headerLeft element to this screen navigationOptions based on its position in the stack, because I either want the default back button to show, or a burger menu icon to show, if the screen is at the root of the stack (because the stack is nested in a drawer).
So I tried the following:
export class ScannerScreen extends Component {
static navigationOptions = ({ navigation }) => ({
headerLeft:
navigation.state.index > 0 ? (
undefined
) : (
<Icon type="ionicon" name="ios-menu" onPress={navigation.toggleDrawer} />
),
title: strings.scannerTitle
});
// ...
The problem is this doesn't work as navigation.state.index is undefined here. How would one do this with React navigation?
Edit:
As per request I added a screenshot of console.log(navigation) (via <Icon />s onPress.)
I found a hacky solution, which is okay but I also dislike it a bit:
const stackConfig: StackNavigatorConfig = {
cardStyle: styles.card,
navigationOptions: {
headerBackTitleStyle: styles.headerBackTitle,
headerStyle: styles.header,
headerTintColor: "black"
}
};
const HomeStack = createStackNavigator(
{ HomeScreen, RestaurantDetailScreen, MenuScreen, ScannerScreen },
{
...stackConfig,
initialRouteName: "HomeScreen",
navigationOptions: ({ navigation }) => ({
headerLeft:
parseFloat(navigation.state.key.slice(-1)) > 1 ? (
undefined
) : (
<Icon
containerStyle={styles.leftIcon}
type="ionicon"
name={getIconName("menu")}
onPress={navigation.toggleDrawer}
/>
),
...stackConfig.navigationOptions
})
}
);
This way it automatically sets it for the whole stack. You can also do this in the respective screen individual navigation options. But this only works, since the automatically assigned key for the screen contains it's index.