I'm want to render the headerLeft conditionally, but it doesn't matter what I do it simply ignores me.
Follow my code:
return (
<Stack.Navigator>
{screens.map((screen, index) => {
const isQuestion = screen.screen === "Question";
return (
<Stack.Screen
key={index}
name={screen.name}
component={getTemplate}
initialParams={{ index, screen }}
options={{
...DefaultNavigationOptions()[screen.theme],
headerBackTitleVisible: false,
title: lessonName,
headerTintColor: isQuestion ? 'red' : '', // <- works!
// headerBackImage: Variable.icons.circle, // <- attempt that did not work
// headerLeft: () => { // <- attempt that did not work
// return null;
// },
headerLeft: props => ( // <- not working
<HeaderBackButton
{...props}
disabled={true}
/>
),
headerRight: () => ( // <- works!
<HeaderButton
handleOnPress={() => onExit()}
icon={Variable.icons.exit}
theme={screen.theme}
/>
),
}}
/>
);
})}
</Stack.Navigator>
);
What I trying for now is simply to change the headerLeft somehow, just to see if it is working so after I can make it conditional.
Already try to find a solution in the docs https://reactnavigation.org/docs/stack-navigator and also in other questions done here on stackoverflow, but no luck so far.
"#react-navigation/native": "^5.7.2",
"#react-navigation/native-stack": "^5.0.5",
"#react-navigation/stack": "^5.8.0",
Would appreciate any help.
So, the problem was that I was using const Stack = createNativeStackNavigator(); instead of const Stack = createStackNavigator();
More info here https://reactnavigation.org/docs/native-stack-navigator/ if someone is interested.
Well i had a similar problem as you, but my solution instead of returning NULL was returning an empty text. That did the work. Why this? Well after reading the documentation it says clearly whatever you write there will replace the back button, so why not an empty Text. XD
//? For some reason NULL wasnt working, so i went with an empty <Text>.
<Stack.Screen
name="Dashboard"
component={DashboardScreen}
options={{headerLeft: () => <Text></Text>}}
/>
Related
I'm having problem in implementing 'goBack' function in React-navigation/drawer 6 ("#react-navigation/drawer": "^6.1.4", precisely).
I was able to perfectly achieve it in react-navigation/drawer 5 with the following codes:
<Drawer.Navigator>
<Drawer.Screen
....
options={{
header: ({ scene }) => {
const { options } = scene.descriptor;
const title = options.headerTitle;
return (
<MyHeader
title={title}
iconName={"menu"}
goback={
()=>scene.descriptor.navigation.goBack()}
/>
);
},
}}
/>
</Drawer.Navigator>
The same revised codes for react-navigation/drawer 6 (as shown below) will take me back to the initial screen (and not on previous screen). It will also give a warning and error message.
<Drawer.Navigator>
<Drawer.Screen
....
options={{
header: ({ navigation, route, options }) => {
const title = getHeaderTitle(options, route.name);
return (
<MyHeader
title={title}
iconName={"menu"}
goback={
()=>navigation.goBack()}
/>
);
},
}}
/>
</Drawer.Navigator>
Please, how can I achieve this 'goBack' in react-navigation/drawer 6?
You need to specify backBehavior
<Drawer.Navigator backBehavior="history">
Please read the upgrade guide when upgrading which documents these changes: https://reactnavigation.org/docs/upgrading-from-5.x/#the-default-value-for-backbehavior-is-now-firstroute-for-tabs-and-drawer
I've faced the same issue and tried to fix it in the way #satya164 provided but then I realised that I just had wrong navigation structure.
If you want to go back from screen B to screen A, they should probably be nested in one Stack rather than in Drawer.
In my case at least it was a better solution.
I'm trying to write logic to show / hide my tabs in my bottom tab navigation in react-nativation (v5).
I've got this kinda working using the tabBarButton property, but for some reason I loose all the styling and they all bunch up to the left.
Here's my code for the bottom tab navigator:
const Tab = createBottomTabNavigator();
<NavigationContainer>
<Tab.Navigator screenOptions={({ route }) => ({
tabBarButton: (props) => {
if (route.name == 'CalendarMain') {
return null;
}
return (<TouchableWithoutFeedback {...props}/>);
}
>
<Tab.Screen ... />
<Tab.Screen ... />
<Tab.Screen ... />
</Tab.Navigator>
</NavigationContainer>
As I say, it works, but the buttons are all pushed up to the left:
You can get active screen in your CalendarMain screen component. Then set it to your redux store. After that connect navigator to redux and if your current screen is CalendarMain - return navigator without tabs, else - with all your tabs.
For example:
if(props.screen == 'CalendarMain'){
return (
<Tab.Navigator ...></Tab.Navigator>
);
else return (
<Tab.Navigator ...>
<Tab.Screen ... />
<Tab.Screen ... />
<Tab.Screen ... />
</Tab.Navigator>
);
Aha, I got it in the end. The key is that you either want to return null (don't render a tab item), or undefined (it's undefined, so use the normal component). This looks like this:
<Tab.Navigator screenOptions={({ route }) => ({
tabBarVisible: true,
tabBarButton: route.name === 'UsersMain' ? () => { return null; } : undefined,
I am new to react native and its navigation modules. I have a simple dashboard.js file where I am using tab navigator like this -
<Tabs.Navigator tabBarOptions={{ activeTintColor: '#ff5757' }}>
<Tabs.Screen
options={{
tabBarIcon: ({ color }) =>
<Icon name='star-border' size={30} padding={15} color={color} />,}}
name={'Orders'}
component={Order}
initialParams={{user}}
/>
<Tabs.Screen
component= {AnotherComponent}
/>
As you can see I am passing InitialParams where I have user props. And I can easily get it in Order component by route.params.
However, in my dashboard component I also have a method that runs every 1 minute and updates user props.
I can't get the updated value of user props in Order component. I am stuck with this for 2 days. In the past I have done like this -
<Tabs.Screen
component = {() => <SomeComponent props= {props}/>}
And it worked fine. But with react-navigation 5 its not working any more.
Please help me if anyone knows. plz.
Thanks a lot.
The initial props seems to be a constant also as per the documentation you have to use redux or context api to update the badge counts in the tabs so I think it will be better to take that approach to handle this problem. Came up with a count changing scenario just like yours using context API.
const CountContext = React.createContext(0);
function HomeScreen() {
return (
<View>
<CountContext.Consumer>
{value => <Text>Home! {value}</Text>}
</CountContext.Consumer>
</View>
);
}
const MyTabs = () => {
const [count, setCount] = React.useState(0);
return (
<CountContext.Provider value={count}>
<View style={{ flex: 1 }}>
<Text>{count}</Text>
<Button title="count" onPress={() => setCount(count + 1)} />
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} options={{ title: 'My home' }} />
<Tab.Screen name="Settings" component={SettingsScreen} options={{ title: 'My home 2' }} />
</Tab.Navigator>
</View>
</CountContext.Provider>
);
};
This way you can skip the navigation params and directly send data to the tab, and this data can be read from other tabs or somewhere down the tree as well.
You can check the full snack here
https://snack.expo.io/#guruparan/5c2b97
I have a nested drawer navigator below, I am using a custom component in the header:
header: props => {
return <DrawerHeader {...props} />;
},
When I try and access from props the current route in my header, like below, the title is undefined, how can I get the current route?
render() {
const {
navigation,
videos,
search: {term},
scene: {
route: {routeName: title}, // undefined
},
} = this.props;
return (
<View>
<View style={styles.container}>
Navigator:
function DrawerStack() {
return (
<Drawer.Navigator>
<Drawer.Screen
name="VideoEpisodesScreen"
component={VideoEpisodesScreen}
/>
<Drawer.Screen name="TestYourselfScreen" component={TestYourselfScreen} />
<Drawer.Screen name="MyResultsScreen" component={MyResultsScreen} />
<Drawer.Screen name="AboutScreen" component={AboutScreen} />
<Drawer.Screen name="TestsScreen" component={TestsScreen} />
<Drawer.Screen
name="BookmarkedVideosScreen"
component={BookmarkedVideosScreen}
/>
</Drawer.Navigator>
);
}
export default function AppNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={stackOptions}
/>
<Stack.Screen
name="Drawer"
component={DrawerStack}
options={drawerOptions}
/>
<Stack.Screen
name="MyResultsScreen"
component={MyResultsScreen}
options={options}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Funnily enough I had the exact same problem and I found your question after it was just an hour old. Essentially the problem is that React Navigation will only give you the current route of the navigator containing the header. If you have a nested navigator, you won't be able to get it.
It looks like this is somewhat intentional, but I've found that by manually querying the state of the navigator, you can drill down to the "deepest" navigator route. Note that while this works for react-navigation 5, it may not work in the future.
You can iteratively query the nested state like this:
const state = navigation.dangerouslyGetState();
let actualRoute = state.routes[state.index];
while (actualRoute.state) {
actualRoute = actualRoute.state.routes[actualRoute.state.index];
}
Note that this is extremely brittle, but it seems to work good enough for my use cases. You should consider creating an issue/feature request on the react-navigation repository for supporting this use case officially.
React Navigation v6 solution:
A nested route object can be discovered through parent Screen listeners. It's given in a callback argument, and can be passed to getFocusedRouteNameFromRoute to get the route name. In the example shown below, I chose to utilize it during the event 'state' (whenever state changes in the nested stack), but you can use it elsewhere if you want.
<Screen
listeners={({ route }) => ({
state: () => {
const subRoute = getFocusedRouteNameFromRoute(route)
// Your logic here //
}
})}
/>
I think in react-navigation 5, you can access route from this.props
const { route } = this.props;
Snapchat's UI is currently setup with a floating SearchBar header that appears to be shared across a few screens/tabs. I'd like to replicate the shared SearchBar header using react-navigation. I currently have a half working solution...
Currently even though I have the headerTitle set on the StackNavigator, it appears that the header is rendering a brand new SearchBar (you can see the slight flicker indicating its rendering) upon navigation to the search results screen.
Here is the setup I have currently for one of the Stacks inside my TabNavigator.
function NetworkStack({ route, navigation }) {
return (
<Network.Navigator
initialRouteName="NetworkEventList"
screenOptions={({ navigation, route }) => ({
headerTitle: () => <Search navigation={navigation} route={route} stackName={"NetworkStack"}/>,
})}>
<Network.Screen
name="NetworkSearchResults"
component={SearchResults}
options={({ navigation, route }) => ({
//headerTitle: () => <Search navigation={navigation} route={route} focused={true} stackName={"NetworkStack"}/>,
headerBackImage: () => <BackButton navigation={navigation} shouldPop={true}/>,
headerBackTitleVisible: false,
gestureEnabled: true
})}/>
<Network.Screen
name="NetworkEventList"
component={NetworkEventList}
options={({ navigation, route }) => ({
headerLeft: () => <ProfileSidebarButton navigation={navigation}/>,
//headerTitle: () => <Search navigation={navigation} focused={false} stackName={"NetworkStack"}/>,
headerRight: () => <CommunityButton navigation={navigation} stackName={"NetworkStack"}/>
})}/>
</Network.Navigator>
)
}
Below is my TabNavigator.
function TabNavigator({ navigation, route }) {
return (
<Tab.Navigator
initialRouteName="NetworkStack"
tabBar={props => <TabBar {...props}/>}>
<Tab.Screen
name="CheckInStack"
component={CheckInStack}/>
<Tab.Screen
name="NetworkStack"
component={NetworkStack}/>
<Tab.Screen
name="MapStack"
component={MapStack}/>
</Tab.Navigator>
);
}
The logic that navigates to the search results component is inside the onFocus listener of the input. Here is the code for that...
const searchBarFocus = () => {
switch(props.stackName) {
case "MapStack":
var searchType = props.searchGoogle ? "AddEstablishment" : "ViewingEstablishments";
props.navigation.navigate('MapSearchResults', {searchType: searchType});
break;
case "NetworkStack":
props.addingMarkers(false);
var searchType = props.searchForPosting ? "ViewingEstablishments" : "ViewingUsers";
let index = null;
let routeState = props.route.state;
if(routeState) index = routeState.index;
if(index !== 1) {
console.log(props.navigation);
props.navigation.navigate('NetworkSearchResults', {searchType: searchType});
}
break;
case "CheckInStack":
props.addingMarkers(false);
props.navigation.navigate('CheckInSearchResults', {searchType: "ViewingUsers"});
break;
}
}
How would I go about configuring my navigation elements so that I have a singular SearchBar element that mounts one time? You can see in the gif I uploaded that the searchbar also loses focus upon navigation, this is also due to the second rendering/mounting of my Search component. Any suggestions would be much appreciated!
I was able to find a solution to my question. Feel free to comment or supply a better answer. I use the react-native-elements Search Bar to create my searching/input element at the header of my navigation stacks. Originally this was a View wrapping the SearchBar component. I changed this to a TouchableOpacity so that I could listen for onPress event. This is the new element I constructed. I kept the original navigation configuration that was supplied in the question.
return (
<TouchableOpacity style={[styles.mainContainer, {width: SEARCH_BAR_WIDTH_UNFOCUSED}]} onPress={() => searchBarPress()}>
<SearchBar
id={"searchBar-"+ props.stackName}
platform="ios"
ref={search => searchRef = search}
value={searchInput}
containerStyle={styles.searchInputContainerWrapper}
inputContainerStyle={styles.searchInputContainer}
inputStyle={{color: styles.inputContainer.color}}
round={true}
autoFocus={props.route.params ? true : false}
pointerEvents={props.route.params ? "auto" : "none"}
cancelButtonTitle="Cancel"
cancelButtonProps={{color: '#707070'}}
onChangeText={updateSearch}
//onFocus={searchBarFocus}
onCancel={() => console.log("Cancel")}
/>
</TouchableOpacity>
)
The key parts of creating the snapchat like search bar header is the autoFocus and pointerEvents properties. The property values needs to be dependent upon which screen the user is currently on.
I think, the good way is to create just a separate component with intention to be a searching component, then when searching happens the results is stored on the global state, as context or redux store
//search.js
const SearchBar = (props) => {...}; //which have access to the global state
//then on your routes
function StackScreen() {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<SearchBar/>
),
}}
/>
<Stack.Screen
name="Users"
component={UsersScreen}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<SearchBar/>
),
}}
/>
</Stack.Navigator>
);
}