React Navigation 5 - How to navigate from headerRight? - reactjs

i still trying to understand this react navigation 5.0.
Fyi i'm using expo, and right now no problem when navigate from one page to other,
problem is when i put navigation for the headerRight.
i put in headerRight in Stack.Navigator because i want this button to be accessible from other screen.
So basically the problem is,
i want to put logout button in headerRight,
but when i try to put navigation.navigate it sait undefined is not an object (evaluating '_this.props')
Then i try to call a function (handleClick),
problem is undefined is not an object too.
May i know what's wrong with my code?
Below is my full code :
import * as React from 'react';
import { Button, View, Text, TextInput, Image, StyleSheet } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import LoginScreen from './src/pages/auth/Login';
import HomeScreen from './src/pages/auth/HomeScreen';
const Stack = createStackNavigator();
export default function App() {
// handleClick = () => {
// this.props.navigation.navigate('Login');
// }
return (
<NavigationContainer>
<Stack.Navigator mode="modal" initialRouteName="Login" screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerRight: () => (
<Button
// only alert is ok, the other is error.
// onPress={() => alert('Success Logout!')}
onPress={this.handleClick}
// onPress={this.props.navigation.navigate('Home')}
title="Logout"
color="#fff"
/>
),
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}>
<Stack.Screen name="Login"
name="Login"
component={LoginScreen}
options={{
title: 'Simple Scorecard',
}} />
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: 'Home Menu',
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Thanks before

You should try this.
You receive the reference to the router (navigation) by converting the property screenOptions to a function.
screenOptions={({route, navigation}) => ({ // get reference to navigation
headerRight: () => (
<Button
onPress={() => navigation.navigate('Home'); // call .navigate on navigation
/>
)
})}

My answer for that.
you have to pass "navigation", and convert "options" to the function
<Stack.Screen
// some code ...
options={({ navigation }) => ({
headerRight: () => (
<TouchableOpacity onPress={() => navigation.navigate("pageYouWantNavigateTo")} >
// some component ...
</TouchableOpacity>
),
})}
/>

Related

Swipeable component doesn't work in BottomTabNavigator

I have a FlatList inside of a screen that uses BottomTabNavigator from react-navigation-bottom-tabs. Inside of the FlatList are swipeable items, using Swipeable from react-native-gesture-handler. Inside of the tab screens these items aren't swipeable for some reason. However, when I put the screen with the FlatList inside of a StackNavigator screen, and nest that inside of the BottomNavigator tabscreen, it does work. I'm at a loss for why it works this way but not the other way. Does anyone have the slightest clue why this is the case?
Edit: I've seen the issue of not being able to navigate bottom tabs using swipe. I'm not trying to navigate tabs though. Swipeable is applied to items inside a FlatList, that is inside its own component, that is rendered by the Home screen, which in turn is used by the navigator.
The TabsNavigator:
export default function TabsNavigator() {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator screenOptions={{ headerShown: false }}>
<Tab.Screen
name="List"
component={HomeStack}
options={{
tabBarIcon: ({ focused }) => (
<FontAwesomeIcon
icon={faList}
style={{ color: focused ? "#104543" : "#CCC" }}
/>
),
tabBarActiveTintColor: "#104543",
}}
/>
)
}
The HomeStack (StackNavigator):
export default function StackNavigator() {
const Stack = createStackNavigator();
return (
<Stack.Navigator>
<Stack.Screen
options={{ headerShown: false }}
name="Home"
component={Home}
/>
</Stack.Navigator>
);
}
The Home screen:
export const Home = ({ navigation }) => {
const dispatch = useDispatch();
useEffect(() => {
const fetchUserData = async () => {
const data = await axios.get(
`http://10.0.2.2:80/
`,
{
auth: {
username: "***",
password: "***",
},
}
);
dispatch(setUsersData(data.data));
};
return navigation.addListener("focus", async () => {
await fetchUserData();
});
}, [navigation]);
return (
<View style={styles.container}>
<SearchBarHeader />
//The component with the FlatList in question that contains swipeable items
<SwipeableFlastList />
</View>
);
};
The preferred outcome would be to place the Home screen inside of the Tab.Screen component like this:
<Tab.Screen
name="List"
component={Home}
/>
Per request, the Swipeable component. It's inside of another ListItem component, which inturn is inside a component that renders the flatlist:
<KeyboardAvoidingView style={styles.container}>
<Swipeable
renderRightActions={(
progress: Animated.AnimatedInterpolation,
dragAnimatedValue: Animated.AnimatedInterpolation
) => renderRightActions(progress, dragAnimatedValue, item.Id)}
renderLeftActions={(
progress: Animated.AnimatedInterpolation,
dragAnimatedValue: Animated.AnimatedInterpolation
) => renderLeftActions(progress, dragAnimatedValue, item.Id)}
useNativeAnimations={true}
onSwipeableOpen={() => setSwipeOpen(!swipeOpen)}
onSwipeableClose={() => setSwipeOpen(!swipeOpen)}
>
<TouchableWithoutFeedback>
<AccordionListItem item={item} />
</TouchableWithoutFeedback>
</Swipeable>
</KeyboardAvoidingView>

change states as well as navigating to another screen

I have a navigation container that calls a function to my stack. I would like to navigate to a tab screen from a stack screen AND change a state in that tab screen. I have given some code below and a demo. In the demo you can see on screen3(stack screen) I am trying to navigate to Home(tab screen) and change a state so that it renders the MapHome screen.
I am unsure how to pass the state to the bottom tab screen without rendering it elsewhere.
I appreciate any insight more than you know.
here is my demo as well as some code below of App.js. You must run the demo in IOS or android, it will not work for web.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import Home from './Home'
import ListHome from './screens/screen1'
import MapHome from './screens/screen2'
import Screen3 from './screens/screen3'
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Stack.Navigator
initialRouteName="Home">
<Stack.Screen name="Home" component= {Home} options={{headerShown: false}}/>
<Stack.Screen name="Screen1" component= {ListHome} options={{headerShown: false}}/>
<Stack.Screen name="Screen2" component= {MapHome} options={{headerShown: false}}/>
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#F60081',
tabBarInactiveTintColor: '#4d4d4d',
tabBarStyle: {
backgroundColor: '#d1cfcf',
borderTopColor: 'transparent',
},
}}
>
<Tab.Screen
name="Home"
component={MyTabs}
options={{
tabBarLabel: 'Home',
headerShown: false,
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Screen3"
component={Screen3}
options={{
tabBarLabel: 'Screen3',
headerShown: false,
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="account-group" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const Stack = createStackNavigator();
You can pass a parameter from Screen3 to the Home screen.
I have forked your demo and did some small refactoring (including changing from Class to Function components just for preference) so you may need to adapt it to your needs. You can find it here.
In Screen3 you can modify your onPress logic to the following:
navigation.navigate('Home', {
screen: 'Home',
params: {
componentToShow: 'Screen2'
}
});
Here's a breakdown of why you are seeing Home twice:
navigation.navigate('Home', { // <-- Tab.Screen name.
screen: 'Home', // <-- Stack.Screen name.
params: {
componentToShow: 'Screen2'
}
});
With this code you are now passing a route.param to the Home screen. I've included a useEffect to run any time route.params changes.
const [whichComponentToShow, setComponentToShow] = React.useState("Screen1");
React.useEffect(() => {
if(route.params && route.params.componentToShow) {
setComponentToShow(route.params.componentToShow);
}
}, [route.params]);
Now, whenever a User navigates to the Home screen and a componentToShow route parameter is provided, the Home screen will update.

onPress function for DrawerNavigator

I have created a DrawerNavigator in my react native app which looks like this.
I just dont like the default header that react- native gives. So I wanna access it through an icon. I guess also using the onPress condition
import { createStackNavigator } from '#react-navigation/stack';
import { createDrawerNavigator } from '#react-navigation/drawer';
// importing of all screens
const Drawer = createDrawerNavigator();
const DrawerContent = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="Home"
component={CategoryStack}
/>
<Drawer.Screen name="Aboutus" component={Aboutus} />
<Drawer.Screen name="Interest Recieved" component={InterestRecieved} />
</Drawer.Navigator>
);
};
const Stack = createStackNavigator();
const MainStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Loading"
component={Loading}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
};
export default MainStack;
How do I open it using an onPress of an icon?
Thanks!
React navigation useNavigation hook expose drawer actions - toggleDrawer,openDrawer and closeDrawer event handlers which you can use to open or close drawer.
import React from "react";
import { View, Text, StyleSheet, Pressable } from "react-native";
import { useNavigation } from "#react-navigation/native";
const ToggleDrawer = () => {
const { toggleDrawer,closeDrawer,openDrawer } = useNavigation();
return (
<Pressable onPress={toggleDrawer}>{/** Add your Icon Here */}</Pressable>
);
};
You can check in-depth drawer example
In the header options you can customize the header and add for example an icon on the top left side like this.
useLayoutEffect(() => {
navigation.setOptions({
title: 'ScreenName',
headerLeft: () => (
<View style={{ marginLeft: 15 }}>
<TouchableOpacity onPress={() => navigation.openDrawer()} >
{*INSERT ICON HERE*}
</TouchableOpacity>
</View>
),
})
})

Delaying Navigation between screens with react navigation

What I want to achieve is when a user clicks on a screen to navigate to another screen, I want the transitioning between the screens to be delayed for 1 second or 2seconds.
I don't know if this is possible in
React navigation
.
I have this code that will transition from home to profile screen.
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
And this is the home screen code
const HomeScreen = ({ navigation }) => {
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigation.navigate('Profile', { name: 'Jane' })
}
/>
);
};
And this is the profile screen code.
const ProfileScreen = ({ navigation, route }) => {
return <Text>This is {route.params.name}'s profile</Text>;
};
};
Is possible to move from home screen to profile screen with a delay in time
As #nithinpp suggested, You could achieve this by adding a setTimeout for 2s in the Home screen button onClick.
const HomeScreen = ({ navigation }) => {
return (
<Button
title="Go to Jane's profile"
onPress={() => {
setTimeout(()=> {
navigation.navigate('Profile', { name: 'Jane' });
}, 2000);
}}
/>
);
};
I don't really see a reason to use this code in any application, as this is not at all a good practice to have a hardcoded delay in your application code.

how to remove tab bar from a particular screen?

I have created a navigator with 4 tab navigator screens->Home, Search, Upload and Library; and I have stack navigation screens like Sign up, log in, home tabs, and video.
Now, I want to remove the bottom tab bar from the Upload screen but am not sure whether it is possible?
Below is the exact code that I have written:
import React from 'react';
import 'react-native-gesture-handler';
import {StatusBar} from 'react-native';
import {NavigationContainer, DarkTheme} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createMaterialBottomTabNavigator} from '#react-navigation/material-bottom-tabs';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialIcons';
import Home from '../screens/Home';
import Search from '../screens/Search';
import Library from '../screens/Library';
import ProfileAuthor from '../screens/ProffileAuthor';
import Shoot from '../screens/Shoot';
import LoginScreen from '../screens/LoginScreen';
import SignUp from '../screens/SignUp';
const Stack = createStackNavigator();
const Tab = createMaterialBottomTabNavigator();
function myTabs() {
return (
<Tab.Navigator
initialRouteName="Home"
activeColor="#000"
inactiveColor="#000"
barStyle={{
backgroundColor: '#FFF',
}}>
<Tab.Screen
name="Home"
component={Home}
options={{
title: 'Home',
tabBarLabel: 'Home',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons color={color} name="home" size={26} />
),
}}
/>
<Tab.Screen
name="Search"
component={Search}
options={{
title: 'Search',
tabBarLabel: 'Search',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons color={color} name="search" size={26} />
),
}}
/>
<Tab.Screen
name="Upload"
component={Shoot}
options={{
title: 'Upload',
tabBarLabel: 'Upload',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons color={color} name="add-box" size={26} />
),
}}
/>
<Tab.Screen
name="Library"
component={Library}
options={{
title: 'Library',
tabBarLabel: 'Library',
tabBarIcon: ({color}) => (
<MaterialCommunityIcons
color={color}
name="person-outline"
size={26}
/>
),
}}
/>
</Tab.Navigator>
);
}
const Routes = () => {
return (
<NavigationContainer theme={DarkTheme}>
<Stack.Navigator>
<Stack.Screen
name="login"
component={LoginScreen}
options={{
header: () => null,
}}
/>
<Stack.Screen
name="signup"
component={SignUp}
options={{
header: () => null,
}}
/>
<Stack.Screen
name="Home"
component={myTabs}
options={{
header: () => null,
}}
/>
<Stack.Screen
name="Video"
component={ProfileAuthor}
options={{
header: () => null,
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default Routes;
could anyone please tell me whether it is possible in this structure, if yes then how to implement? any help would be great.
tabBarVisible: false
It is working when you used createBottomTabNavigator from #react-navigation/bottom-tabs. but if you will use createMaterialBottomTabNavigator from #react-navigation/material-bottom-tabs to create bottom tabs, it's not working. createMaterialBottomTabNavigator can't hide the tab bar, you can see that there is no such option in docs. You should try to nest the bottom-tabs navigator inside the stack navigator if you want it to disappear on the other screens of the stack navigator. The below code is for the hide tab bar in createBottomTabNavigator.
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const BottomTabs = createBottomTabNavigator();
<BottomTabs.Navigator>
<BottomTabs.Screen name={AppRoute.MORE} component={MoreStack}
options={({ route }) => ({
tabBarVisible: getTabBarVisibility(route),
tabBarLabel: languages.stack_More
})}
/>
</BottomTabs.Navigator>
const getTabBarVisibility = (route: any) => {
const routeName = route.state ? route.state.routes[route.state.index].name: '';
if (routeName === AppRoute.PROFILE
|| routeName === AppRoute.HELP_CENTER
|| routeName === AppRoute.TERMS_CONDITION) {
return false;
}
return true;
}

Resources