Adding a back button for each bottom tab with React Navigation - react-native

The bottom tabs navigation is looking something like that:
const Tab = createBottomTabNavigator();
export default function TabStackScreen() {
return (
<Tab.Navigator initialRouteName="Home">
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Favorites" component={FavoritesStackScreen} />
<Tab.Screen name="Search" component={SearchStackScreen} />
</Tab.Navigator>
)
}
There is not back button on the favorites and search screen. I guess this is the normal behavior, but I would love to have one.
I did not find something relevant in the doc, except recreating a component that looks like the native back button and adding it on some screens using headerLeft. Is there a more simple method?

In my projects I like to create custom headers, and I do not show the default header and show my own custom header component.
On my custom component I add right and left components, and let the screens decide what to do, by default the back button is shown, but if the screen pass the noBack property, the button is hidden.
You can also add a right component, for example a close button.
That is what I use to do:
const screenOptions = {
headerShown: false
};
<RootStack.Navigator screenOptions={screenOptions} mode="modal">
and then create my own component
export const Header = ({ title, leftComponent, rightComponent, noBack }) => {
const navigation = useNavigation();
return (
<Wrapper>
{leftComponent ||
(noBack ? (
<Placeholder />
) : (
<Button
onPress={() => navigation.goBack()}
accessible
accessibilityRole="button"
accessibilityLabel="Back">
<Icon
size={30}
name={Platform.OS === 'android' ? 'arrow-left' : 'chevron-left'}
/>
</Button>
))}
<Title bold accessibilityRole="header" acessible acessibilityText={title}>
{title}
</Title>
{rightComponent || <Placeholder />}
</Wrapper>
);
};
Doing that, you are able to customize everything on your header, it works like a charm for me.

Related

How do i skip the animation only replace with react-navigation

i am using #react-navigation/stack^5.14.4 and #react-navigation/native^5.9.4, to handle the scene transition between Home, Login and Profile pages.
there are 2 transition cases i need to handle:
Home -> Profile (should have animation enabled)
Login -> Profile (need to skip the animation)
the reason why to skip the animation is that I am using a loading skeleton similar to the layout of Profile page during the login process. It is weird to have a transition between the profile page content and the skeleton itself. However I want to keep the awesome animation if pushed from Home page.
Is there a simple solution like
navigation.replace("Profile"); // with animation
navigation.replace("Profile", { animationEnabled: false }); // skip animation
You can use options to get the route property and check weather there is any parameter to disable the screen
snak: https://snack.expo.dev/#ashwith00/react-navigation-5-boilerplate
heres is an example
function TestScreen({ navigation }) {
return (
<View style={styles.content}>
<Button
mode="contained"
onPress={() => navigation.push('Test1', {disabledAnimation: true})}
style={styles.button}>
Push screen Without Animation
</Button>
<Button
mode="contained"
onPress={() => navigation.push('Test1')}
style={styles.button}>
Push new screen
</Button>
{navigation.canGoBack() ? (
<Button
mode="outlined"
onPress={() => navigation.pop()}
style={styles.button}>
Go back
</Button>
) : null}
</View>
);
}
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen
name="Test"
component={TestScreen}
options={{ title: 'Custom animation' }}
/>
<Stack.Screen
name="Test1"
component={TestScreen}
options={({ route }) => ({
title: 'Custom animation',
cardStyleInterpolator: !route.params?.disabledAnimation
? undefined
: CardStyleInterpolators.forNoAnimation,
})}
/>
</Stack.Navigator>
);
}
i find a more simple way to make it:
firstly, export the Stack.Screen node from the router file.
export const ProfileStack = (
<Stack.Screen name={'Profile'} component={Profile} />
);
export default Router = () => (
<Stack.Navigator>
<Stack.Screen name={'Home'} component={Home} />
<Stack.Screen name={'Login'} component={Login} />
{ProfileStack}
</Stack.Navigator>
);
then I can execute like this to disable the animation once.
with animation:
<Button onClick={() => {
navigation.replace('Profile');
}} />
without animation
<Button onClick={() => {
ProfileStack.props.options.animationEnabled = false;
navigation.replace('Profile');
ProfileStack.props.options.animationEnabled = true;
}} />

Tab navigation lag when nested in drawer

I am having a issue when I am nesting my Tabs within my Drawer. Unfortunately, navigating to each tab is very slow, and their seems to be a lot of lag.
However, when I remove the Drawer navigator, and make it so that their is only a tab navigator, navigating between the different tab screens is noticeably better.
How can I make it so that their is no delay between the tabs when the tabs are nested in to the drawer?
{ *
With help from Mateusz, I have managed to pinpoint the issue. I tested the delay by rendering four of the same components. The first test was using
children={() => {
return <NfcWifiConfig />;
}}
And the delay was still there
But then, when I used
component={NfcWifiConfig}
The delay is completely gone and navigation is running smoothly as it should. So my question now is, where do i go from here? How would i pass the props down with this syntax?
}
My current code is:
const DrawerComponent = ({
Bunch of props here
}) => {
return (
<Drawer.Navigator
drawerType="back"
drawerContent={(props) => {
return (
<DrawerContent
{...props}
/>
);
}}
>
{/* TABS */}
<Drawer.Screen
name="MainHome"
children={({navigation}) => {
return (
<>
<StatusBar backgroundColor={homeColor} barStyle="dark-content" />
<Navbar navigation={navigation} userimage={userimage} />
<Tabs.Navigator>
{/* HOME STACK */}
<Tabs.Screen
name="Profile"
children={() => {
return (
<>
<MainStackNavigator
{Bunch of props here}
/>
</>
;
}}
/>
{/* SEARCH SCREEN */}
<Tabs.Screen
name="Search"
children={() => {
return (
<>
<StatusBar barStyle="dark-content" />
<SearchStack
{ Bunch of props here }
/>
</>
);
}}
/>
{/* NFC-SOCIAL SCREEN */}
<Tabs.Screen name="Activate" component={NfcConfig} />
{/* NFC-WIFI SCREEN */}
<Tabs.Screen name="WiFi" component={NfcWifiConfig} />
</Tabs.Navigator>
</>
);
}}
/>
{/* Add Links Screen */}
<Drawer.Screen
name="Add Links"
children={({navigation}) => {
return (
<AddLinksScreen
{ Bunch of props here }
/>
);
}}
/>
{/* Following Screen */}
<Drawer.Screen
name="Followers"
children={({navigation}) => {
return (
<FollowerStack
{ Bunch of props here }
/>
);
}}
/>
{/* Following Screen */}
<Drawer.Screen
name="Following"
children={({navigation}) => {
return (
<FollowingStack
{ Bunch of props here }
/>
);
}}
/>
</Drawer.Navigator>
);
};
Also, the add links screen and followers/following screens work fine. Navigating to them works efficiently with no lag. But the tabs => home stack, search screen and the other two, have a heavy delay when navigating between them.
In terms of the content inside the tabs, the last two tabs are very light, and do not contain much content. I have tried commenting out the heavy tab screens and using just the two lightweight components, but same result. Making me believe that is not the issue.
So I managed to fix the issue. When I used:
children={() => {
return <NfcWifiConfig props{props} />;
}}
The lag was present. However, when I used:
component={NfcWifiConfig}
The lag disappeared. However, my props were not being passed through. So what I did was use React Context to pass my props to all the different components that needed it and that's it, the lag was gone and the components were receiving the props as I wanted.
Also, the code is a lot cleaner when using React context, so I highly recommend it.

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: ...
})

Trying to add show/hide logic for tab buttons

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,

React native navigation 5 passing updated component

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