Skip to content

Commit 5428315

Browse files
spokodevthiagobrez
andauthored
fix(TabView): respect user-supplied tabBarHidden (#524)
* fix(TabView): respect user-supplied tabBarHidden The native side wired tabBarHidden into the SwiftUI tab bar via #76, but the JS wrapper hardcoded tabBarHidden={!!renderCustomTabBar} after the {...props} spread, so any value the caller passed was overwritten before it reached NativeTabView. The prop was also absent from the TabView Props interface so it never type-checked at all. Add tabBarHidden to Props, destructure it explicitly, and keep the renderCustomTabBar default only as a fallback when the caller does not supply a value. Closes #521 * chore: address PR comments * chore: add tabBarHidden examples * refactor(TabView): drop unreachable tabBarHidden fallback With tabBarHidden = false now defaulted at the destructure (commit 8d45171), the ?? !!renderCustomTabBar fallback at the JSX render is dead code -- the left operand is never nullish, so the right operand is never evaluated. Simplifying to tabBarHidden={tabBarHidden} for clarity. Note: this drops the pre-existing auto-hide-when-renderCustomTabBar behavior. Callers using a custom tabBar who relied on the implicit hide now need to set tabBarHidden={true} explicitly. * chore: fix custom tabBar behavior with tabBarHidden and add more examples --------- Co-authored-by: Thiago Brezinski <thiagobrez@gmail.com>
1 parent 6797d32 commit 5428315

6 files changed

Lines changed: 302 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-native-bottom-tabs': patch
3+
---
4+
5+
Respect user-supplied `tabBarHidden` on `TabView`.

apps/example/src/App.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ import NativeBottomTabsFreezeOnBlur from './Examples/NativeBottomTabsFreezeOnBlu
3838
import NativeBottomTabsScreenLayout from './Examples/NativeBottomTabsScreenLayout';
3939
import NativeBottomTabsLazy from './Examples/NativeBottomTabsLazy';
4040
import BottomAccessoryView from './Examples/BottomAccessoryView';
41+
import TabBarHidden from './Examples/TabBarHidden';
42+
import CustomTabBar from './Examples/CustomTabBar';
43+
import NativeBottomTabsTabBarHidden from './Examples/NativeBottomTabsTabBarHidden';
4144
import { useLogger } from '@react-navigation/devtools';
4245
import LazyTabs from './Examples/LazyTabs';
4346
import { LogBox } from 'react-native';
@@ -103,6 +106,14 @@ const examples = [
103106
screenOptions: { headerShown: false },
104107
},
105108
{ component: LazyTabs, name: 'Lazy Tabs' },
109+
{
110+
component: TabBarHidden,
111+
name: 'Tab Bar Hidden',
112+
},
113+
{
114+
component: CustomTabBar,
115+
name: 'Custom tabBar',
116+
},
106117
{
107118
component: FourTabsRippleColor,
108119
name: 'Four Tabs with ripple Color',
@@ -157,7 +168,7 @@ const examples = [
157168
},
158169
{
159170
component: NativeBottomTabsCustomTabBar,
160-
name: 'Native Bottom Tabs with Custom Tab Bar',
171+
name: 'Native Bottom Tabs with custom tabBar',
161172
},
162173
{
163174
component: NativeBottomTabsScreenLayout,
@@ -167,6 +178,10 @@ const examples = [
167178
component: NativeBottomTabsLazy,
168179
name: 'Native Bottom Tabs with Lazy',
169180
},
181+
{
182+
component: NativeBottomTabsTabBarHidden,
183+
name: 'Native Bottom Tabs with tabBarHidden',
184+
},
170185
{ component: NativeBottomTabs, name: 'Native Bottom Tabs' },
171186
{ component: JSBottomTabs, name: 'JS Bottom Tabs' },
172187
{
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import TabView from 'react-native-bottom-tabs';
2+
import { useState } from 'react';
3+
import { Pressable, StyleSheet, Text, View } from 'react-native';
4+
5+
const routes = [
6+
{
7+
key: 'article',
8+
title: 'Article',
9+
focusedIcon: require('../../assets/icons/article_dark.png'),
10+
},
11+
{
12+
key: 'albums',
13+
title: 'Albums',
14+
focusedIcon: require('../../assets/icons/grid_dark.png'),
15+
},
16+
{
17+
key: 'contacts',
18+
title: 'Contacts',
19+
focusedIcon: require('../../assets/icons/person_dark.png'),
20+
},
21+
];
22+
23+
export default function CustomTabBar() {
24+
const [index, setIndex] = useState(0);
25+
26+
return (
27+
<TabView
28+
sidebarAdaptable
29+
navigationState={{ index, routes }}
30+
onIndexChange={setIndex}
31+
renderScene={({ route }) => (
32+
<View style={styles.screen}>
33+
<Text style={styles.title}>{route.title}</Text>
34+
</View>
35+
)}
36+
tabBar={() => (
37+
<View style={styles.customTabBar}>
38+
{routes.map((route, routeIndex) => {
39+
const focused = routeIndex === index;
40+
41+
return (
42+
<Pressable
43+
key={route.key}
44+
style={[
45+
styles.customTabBarItem,
46+
focused && styles.customTabBarItemFocused,
47+
]}
48+
onPress={() => setIndex(routeIndex)}
49+
>
50+
<Text
51+
style={[
52+
styles.customTabBarLabel,
53+
focused && styles.customTabBarLabelFocused,
54+
]}
55+
>
56+
{route.title}
57+
</Text>
58+
</Pressable>
59+
);
60+
})}
61+
</View>
62+
)}
63+
/>
64+
);
65+
}
66+
67+
const styles = StyleSheet.create({
68+
screen: {
69+
flex: 1,
70+
alignItems: 'center',
71+
justifyContent: 'center',
72+
padding: 24,
73+
},
74+
title: {
75+
fontSize: 24,
76+
fontWeight: '600',
77+
},
78+
customTabBar: {
79+
flexDirection: 'row',
80+
gap: 8,
81+
paddingHorizontal: 12,
82+
paddingTop: 10,
83+
paddingBottom: 18,
84+
backgroundColor: '#24292f',
85+
},
86+
customTabBarItem: {
87+
flex: 1,
88+
alignItems: 'center',
89+
justifyContent: 'center',
90+
minHeight: 44,
91+
borderRadius: 6,
92+
backgroundColor: '#3b434c',
93+
},
94+
customTabBarItemFocused: {
95+
backgroundColor: '#ffffff',
96+
},
97+
customTabBarLabel: {
98+
color: '#ffffff',
99+
fontWeight: '600',
100+
},
101+
customTabBarLabelFocused: {
102+
color: '#24292f',
103+
},
104+
});
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
2+
import { useState } from 'react';
3+
import { Button, StyleSheet, Text, View } from 'react-native';
4+
5+
const Tab = createNativeBottomTabNavigator();
6+
7+
type TabBarHiddenScreenProps = {
8+
title: string;
9+
tabBarHidden: boolean;
10+
onToggleTabBarHidden: () => void;
11+
};
12+
13+
function TabBarHiddenScreen({
14+
title,
15+
tabBarHidden,
16+
onToggleTabBarHidden,
17+
}: TabBarHiddenScreenProps) {
18+
return (
19+
<View style={styles.screen}>
20+
<Text style={styles.title}>{title}</Text>
21+
<Button
22+
title={`${tabBarHidden ? 'Show' : 'Hide'} Tab Bar`}
23+
onPress={onToggleTabBarHidden}
24+
/>
25+
</View>
26+
);
27+
}
28+
29+
export default function NativeBottomTabsTabBarHidden() {
30+
const [tabBarHidden, setTabBarHidden] = useState(false);
31+
const toggleTabBarHidden = () => setTabBarHidden((value) => !value);
32+
33+
return (
34+
<Tab.Navigator sidebarAdaptable tabBarHidden={tabBarHidden}>
35+
<Tab.Screen
36+
name="Article"
37+
options={{
38+
tabBarIcon: () => require('../../assets/icons/article_dark.png'),
39+
}}
40+
>
41+
{() => (
42+
<TabBarHiddenScreen
43+
title="Article"
44+
tabBarHidden={tabBarHidden}
45+
onToggleTabBarHidden={toggleTabBarHidden}
46+
/>
47+
)}
48+
</Tab.Screen>
49+
<Tab.Screen
50+
name="Albums"
51+
options={{
52+
tabBarIcon: () => require('../../assets/icons/grid_dark.png'),
53+
}}
54+
>
55+
{() => (
56+
<TabBarHiddenScreen
57+
title="Albums"
58+
tabBarHidden={tabBarHidden}
59+
onToggleTabBarHidden={toggleTabBarHidden}
60+
/>
61+
)}
62+
</Tab.Screen>
63+
<Tab.Screen
64+
name="Contacts"
65+
options={{
66+
tabBarIcon: () => require('../../assets/icons/person_dark.png'),
67+
}}
68+
>
69+
{() => (
70+
<TabBarHiddenScreen
71+
title="Contacts"
72+
tabBarHidden={tabBarHidden}
73+
onToggleTabBarHidden={toggleTabBarHidden}
74+
/>
75+
)}
76+
</Tab.Screen>
77+
</Tab.Navigator>
78+
);
79+
}
80+
81+
const styles = StyleSheet.create({
82+
screen: {
83+
flex: 1,
84+
alignItems: 'center',
85+
justifyContent: 'center',
86+
gap: 16,
87+
padding: 24,
88+
},
89+
title: {
90+
fontSize: 24,
91+
fontWeight: '600',
92+
},
93+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import TabView from 'react-native-bottom-tabs';
2+
import { useState } from 'react';
3+
import { Button, StyleSheet, Text, View } from 'react-native';
4+
5+
const routes = [
6+
{
7+
key: 'article',
8+
title: 'Article',
9+
focusedIcon: require('../../assets/icons/article_dark.png'),
10+
},
11+
{
12+
key: 'albums',
13+
title: 'Albums',
14+
focusedIcon: require('../../assets/icons/grid_dark.png'),
15+
},
16+
{
17+
key: 'contacts',
18+
title: 'Contacts',
19+
focusedIcon: require('../../assets/icons/person_dark.png'),
20+
},
21+
];
22+
23+
type TabBarHiddenScreenProps = {
24+
title: string;
25+
tabBarHidden: boolean;
26+
onToggleTabBarHidden: () => void;
27+
};
28+
29+
function TabBarHiddenScreen({
30+
title,
31+
tabBarHidden,
32+
onToggleTabBarHidden,
33+
}: TabBarHiddenScreenProps) {
34+
return (
35+
<View style={styles.screen}>
36+
<Text style={styles.title}>{title}</Text>
37+
<Button
38+
title={`${tabBarHidden ? 'Show' : 'Hide'} Tab Bar`}
39+
onPress={onToggleTabBarHidden}
40+
/>
41+
</View>
42+
);
43+
}
44+
45+
export default function TabBarHidden() {
46+
const [index, setIndex] = useState(0);
47+
const [tabBarHidden, setTabBarHidden] = useState(false);
48+
49+
return (
50+
<TabView
51+
sidebarAdaptable
52+
navigationState={{ index, routes }}
53+
onIndexChange={setIndex}
54+
renderScene={({ route }) => (
55+
<TabBarHiddenScreen
56+
title={route.title}
57+
tabBarHidden={tabBarHidden}
58+
onToggleTabBarHidden={() => setTabBarHidden((value) => !value)}
59+
/>
60+
)}
61+
tabBarHidden={tabBarHidden}
62+
/>
63+
);
64+
}
65+
66+
const styles = StyleSheet.create({
67+
screen: {
68+
flex: 1,
69+
alignItems: 'center',
70+
justifyContent: 'center',
71+
gap: 16,
72+
padding: 24,
73+
},
74+
title: {
75+
fontSize: 24,
76+
fontWeight: '600',
77+
},
78+
});

packages/react-native-bottom-tabs/src/TabView.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ interface Props<Route extends BaseRoute> {
220220
* @default 'locale'
221221
*/
222222
layoutDirection?: LayoutDirection;
223+
/**
224+
* Whether to hide the native tab bar.
225+
*/
226+
tabBarHidden?: boolean;
223227
}
224228

225229
const ANDROID_MAX_TABS = 100;
@@ -255,6 +259,7 @@ const TabView = <Route extends BaseRoute>({
255259
labeled = Platform.OS !== 'android' ? true : undefined,
256260
getFreezeOnBlur = ({ route }: { route: Route }) => route.freezeOnBlur,
257261
tabBar: renderCustomTabBar,
262+
tabBarHidden,
258263
tabBarStyle,
259264
tabLabelStyle,
260265
renderBottomAccessoryView,
@@ -413,7 +418,7 @@ const TabView = <Route extends BaseRoute>({
413418
// When rendering a custom tab bar, icons can be React elements, which will not be properly resolved.
414419
icons={renderCustomTabBar ? undefined : resolvedIconAssets}
415420
selectedPage={focusedKey}
416-
tabBarHidden={!!renderCustomTabBar}
421+
tabBarHidden={tabBarHidden ?? !!renderCustomTabBar}
417422
onTabLongPress={handleTabLongPress}
418423
onPageSelected={handlePageSelected}
419424
onTabBarMeasured={handleTabBarMeasured}

0 commit comments

Comments
 (0)